You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

362 lines
15 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.Caching;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using TradeManageNew.APIClients.FedexApi.Models;
// ReSharper disable InconsistentNaming
namespace TradeManageNew.APIClients.FedexApi.Services
{
public class RequestService
{
private static Lazy<JsonSerializerSettings> _settings =
new Lazy<JsonSerializerSettings>(CreateSerializerSettings, true);
private JsonSerializerSettings _instanceSettings;
protected RequestService(FedexCredential credential)
{
BaseUrl = credential.Host;
ClientId = credential.ClientId;
ClientSecret = credential.ClientSecret;
ChildKey = credential.ChildKey;
ChildSecret = credential.ChildSecret;
ReadResponseAsString = credential.ReadResponseAsString;
Initialize();
}
protected string _baseUrl;
private string BaseUrl
{
get => _baseUrl;
set
{
_baseUrl = value;
if (!string.IsNullOrEmpty(_baseUrl) && !_baseUrl.EndsWith("/"))
_baseUrl += '/';
}
}
private string ClientId { get; set; }
private string ClientSecret { get; set; }
private string ChildKey { get; set; }
private string ChildSecret { get; set; }
private bool ReadResponseAsString { get; set; }
private static void Initialize()
{
//启用TLS1.2
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
}
private static JsonSerializerSettings CreateSerializerSettings()
{
var settings = new JsonSerializerSettings();
return settings;
}
protected JsonSerializerSettings JsonSerializerSettings => _instanceSettings ?? _settings.Value;
protected void PrepareRequest(HttpClient client, HttpRequestMessage request,
string url)
{
var token = GetAccessTokenAsync()
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
//添加授权信息
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
protected void PrepareRequest(HttpClient client, HttpRequestMessage request,
StringBuilder urlBuilder)
{
}
protected static void ProcessResponse(HttpClient client,
HttpResponseMessage response)
{
}
private async Task<string> GetAccessTokenAsync()
{
MemoryCache memoryCache = MemoryCache.Default;
string fedexAccessTokenKey = $"{MemoryCacheKeys.FEDEX_ACCESS_TOKEN_PREFIX}_{ClientId}";
string fedexAccessTokenValue = memoryCache.Get(fedexAccessTokenKey) as string;
if (string.IsNullOrWhiteSpace(fedexAccessTokenValue))
{
int tryCount = 0;
int maxTryCount = 3;
while (tryCount < maxTryCount)
{
try
{
var fedexOauthTokenResp = await OauthTokenAsync();
// 设置缓存策略(如过期时间)
CacheItemPolicy policy = new CacheItemPolicy
{
AbsoluteExpiration =
DateTimeOffset.Now.AddSeconds(fedexOauthTokenResp.Expires_in - 60)
};
// 添加数据到缓存
memoryCache.Set(fedexAccessTokenKey, fedexOauthTokenResp.Access_token, policy);
return fedexOauthTokenResp.Access_token;
}
catch (Exception ex)
{
tryCount++;
if (tryCount == maxTryCount)
{
throw new Exception($"获取Fedex AccessToken失败错误信息{ex.Message}");
}
}
}
}
return fedexAccessTokenValue;
}
private async Task<FedexOauthTokenResp> OauthTokenAsync()
{
var client_ = new HttpClient();
var disposeClient_ = true;
try
{
using (var request_ = new HttpRequestMessage())
{
var postData =
new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", ClientId),
new KeyValuePair<string, string>("client_secret", ClientSecret),
new KeyValuePair<string, string>("grant_type",
"client_credentials"),
};
var content_ = new FormUrlEncodedContent(postData);
content_.Headers.ContentType =
MediaTypeHeaderValue.Parse("application/x-www-form-urlencoded");
request_.Content = content_;
request_.Method = new HttpMethod("POST");
request_.Headers.Accept.Add(
MediaTypeWithQualityHeaderValue.Parse("application/json"));
var urlBuilder_ = new StringBuilder();
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
// Operation Path: "oauth/token"
urlBuilder_.Append("oauth/token");
var url_ = urlBuilder_.ToString();
request_.RequestUri = new Uri(url_, UriKind.RelativeOrAbsolute);
var response_ = await client_.SendAsync(request_,
HttpCompletionOption.ResponseHeadersRead,
CancellationToken.None);
var disposeResponse_ = true;
try
{
var headers_ =
new Dictionary<string,
IEnumerable<string>>();
foreach (var item_ in response_.Headers)
headers_[item_.Key] = item_.Value;
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
var objectResponse_ =
await ReadObjectResponseAsync<FedexOauthTokenResp>(response_, headers_,
CancellationToken.None).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new FedexAPIException("Response was null which was not expected.", status_,
objectResponse_.Text, headers_, null);
}
return objectResponse_.Object;
}
else if (status_ == 401)
{
var objectResponse_ =
await ReadObjectResponseAsync<ErrorResponse>(response_, headers_,
CancellationToken.None).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new FedexAPIException("Response was null which was not expected.", status_,
objectResponse_.Text, headers_, null);
}
throw new FedexAPIException<ErrorResponse>("Unauthorized", status_, objectResponse_.Text,
headers_, objectResponse_.Object, null);
}
else if (status_ == 500)
{
var objectResponse_ =
await ReadObjectResponseAsync<ErrorResponse>(response_, headers_,
CancellationToken.None).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new FedexAPIException("Response was null which was not expected.", status_,
objectResponse_.Text, headers_, null);
}
throw new FedexAPIException<ErrorResponse>("Failure", status_, objectResponse_.Text,
headers_, objectResponse_.Object, null);
}
else if (status_ == 503)
{
var objectResponse_ =
await ReadObjectResponseAsync<ErrorResponse>(response_, headers_,
CancellationToken.None).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new FedexAPIException("Response was null which was not expected.", status_,
objectResponse_.Text, headers_, null);
}
throw new FedexAPIException<ErrorResponse>("Service Unavailable", status_,
objectResponse_.Text, headers_, objectResponse_.Object, null);
}
else
{
var responseData_ = response_.Content == null
? null
: await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new FedexAPIException(
"The HTTP status code of the response was not expected (" + status_ + ").", status_,
responseData_, headers_, null);
}
}
finally
{
if (disposeResponse_)
response_.Dispose();
}
}
}
finally
{
if (disposeClient_)
client_.Dispose();
}
}
protected struct ObjectResponseResult<T>
{
public ObjectResponseResult(T responseObject, string responseText)
{
Object = responseObject;
Text = responseText;
}
public T Object { get; }
public string Text { get; }
}
protected async Task<ObjectResponseResult<T>> ReadObjectResponseAsync<T>(
HttpResponseMessage response,
IReadOnlyDictionary<string, IEnumerable<string>>
headers, CancellationToken cancellationToken)
{
if (response == null || response.Content == null)
{
return new ObjectResponseResult<T>(default(T), string.Empty);
}
if (ReadResponseAsString)
{
var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
try
{
var typedBody =
JsonConvert.DeserializeObject<T>(responseText, JsonSerializerSettings);
return new ObjectResponseResult<T>(typedBody, responseText);
}
catch (JsonException exception)
{
var message = "Could not deserialize the response body string as " + typeof(T).FullName + ".";
throw new FedexAPIException(message, (int)response.StatusCode, responseText, headers, exception);
}
}
try
{
using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
using (var streamReader = new StreamReader(responseStream))
using (var jsonTextReader = new JsonTextReader(streamReader))
{
var serializer = JsonSerializer.Create(JsonSerializerSettings);
var typedBody = serializer.Deserialize<T>(jsonTextReader);
return new ObjectResponseResult<T>(typedBody, string.Empty);
}
}
catch (JsonException exception)
{
var message = "Could not deserialize the response body stream as " + typeof(T).FullName + ".";
throw new FedexAPIException(message, (int)response.StatusCode, string.Empty, headers, exception);
}
}
}
/// <summary>
/// This is the response of OAuth token and having access token details.
/// </summary>
[GeneratedCode("NJsonSchema",
"14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")]
public class FedexOauthTokenResp
{
/// <summary>
/// This is an encrypted OAuth token used to authenticate your API requests. Use it in the authorization header of your API requests.&lt;br&gt;Example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpX……
/// </summary>
[JsonProperty("access_token", Required = Required.Default,
NullValueHandling = NullValueHandling.Ignore)]
public string Access_token { get; set; }
/// <summary>
/// This is a token type. In this case, it is 'bearer authentication'.
/// </summary>
[JsonProperty("token_type", Required = Required.Default,
NullValueHandling = NullValueHandling.Ignore)]
public string Token_type { get; set; }
/// <summary>
/// Indicates the token expiration time in seconds. The standard token expiration time is one hour. &lt;br&gt;Example: 3600
/// </summary>
[JsonProperty("expires_in", Required = Required.Default,
NullValueHandling = NullValueHandling.Ignore)]
public int Expires_in { get; set; }
/// <summary>
/// Indicates the scope of authorization provided to the consumer.&lt;br&gt; Example: CXS
/// </summary>
[JsonProperty("scope", Required = Required.Default,
NullValueHandling = NullValueHandling.Ignore)]
public string Scope { get; set; }
}
}