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#

2 months ago
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 TradeManageNew.APIClients.FedexAPI.Models;
using Newtonsoft.Json;
// 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_KEY;
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; }
}
}