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 ; }
}
}