|
|
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.<br>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. <br>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.<br> Example: CXS
|
|
|
/// </summary>
|
|
|
[JsonProperty("scope", Required = Required.Default,
|
|
|
NullValueHandling = NullValueHandling.Ignore)]
|
|
|
public string Scope { get; set; }
|
|
|
}
|
|
|
} |