最近在做的一個功能是通過ESB調用http的client來發送Http請求,學習了相關的調用方式,如何讓請求帶有重試機制的發送呢?
HttpClient初始化
在整個調用過程中,我們使用到了委託方法的方式,在外層的委託里加入了重試機制以及線程的休眠機制。然後委託調用的方法又分爲POST和Get,同時我還使用了返回結果泛型類的方式來定義響應情況,包括成功還是失敗的響應狀態碼。
/// <summary>
/// 用於訪問Rest接口(訪問站點的請求)
/// </summary>
public class HttpClientHelper
{
#region 日誌及單例
protected static readonly LogWrapper Logger = new LogWrapper();
#endregion 日誌及單例
#region 委託方式進行重試調用
public static TResult ExecuteFunc<TResult>(Func<TResult> target, int retryCount = 5, int current = 1, int sleepMilliseconds = 0)
{
try
{
return target.Invoke();
}
catch (Exception)
{
//超過重試次數後拋出異常
if (retryCount - current <= 0)
{
throw;
}
if (sleepMilliseconds > 0)
{
Thread.Sleep(sleepMilliseconds);
}
}
//遞歸調用直至超出重試次數後拋出異常
return ExecuteFunc(target, retryCount, current + 1, sleepMilliseconds);
}
#endregion 委託方式進行重試調用
#region POST及GET請求方法
/// <summary>
/// 通過post請求數據
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <typeparam name="TData"></typeparam>
/// <param name="url"></param>
/// <param name="data"></param>
/// <returns></returns>
public static ApiResult<TResult> Post<TResult, TData>(string url, TData data)
{
//獲取請求數據
var value = data == null ? string.Empty : JsonConvert.SerializeObject(data);
//封裝有關個別HTTP請求的所有HTTP特定的信息(上下文信息)
var content = new StringContent(value, Encoding.UTF8);
//設置請求頭的上下文類型
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
//發送Post異步請求信息
using (var client = new HttpClient())
{
//發送異步請求
var result = client.PostAsync(url, content).Result;
//獲取請求返回的結果數據並將其序列化爲字符串
var response = result.Content.ReadAsStringAsync().Result;
if (result.StatusCode != System.Net.HttpStatusCode.OK)
throw new HttpRequestException($"調用接口:{url}報錯,StatusCode:{result.StatusCode},Msg:{response}");
//將返回結果反序列化爲指定Model
return JsonConvert.DeserializeObject<ApiResult<TResult>>(response);
}
}
/// <summary>
/// 通過Get方式請求數據
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <returns></returns>
public static ApiResult<T> Get<T>(string url)
{
//發送Get異步請求信息
using (var client = new HttpClient())
{
//發送異步請求
var result = client.GetAsync(url).Result;
//獲取請求返回的結果數據並將其序列化爲字符串
var response = result.Content.ReadAsStringAsync().Result;
if (result.StatusCode != System.Net.HttpStatusCode.OK)
throw new HttpRequestException($"調用接口:{url}報錯,StatusCode:{result.StatusCode},Msg:{response}");
//將返回結果反序列化爲指定Model
return JsonConvert.DeserializeObject<ApiResult<T>>(response);
}
}
#endregion POST及GET請求方法
}
/// <summary>
/// 返回結果類
/// </summary>
/// <typeparam name="T"></typeparam>
public class ApiResult<T>
{
private static readonly string SUCCESS = "200";
private static readonly string FAIL = "500";
public string Code { get; set; }
public string Message { get; set; }
public T Data { get; set; }
public int Total { get; set; }
/// <summary>
/// 訪問成功
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static ApiResult<T> Success(T data)
{
return new ApiResult<T>()
{
Data = data,
Code = SUCCESS
};
}
/// <summary>
/// 訪問失敗
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public static ApiResult<T> Fail(string message)
{
return new ApiResult<T>()
{
Code = FAIL,
Message = message
};
}
接口調用
在定義好了HttpClient之後,我們就可以通過接口調用的方式來啓動對站點的訪問了,這部分因爲我們的環境不同,所以域名需要不同,所以域名就需要通過配置來讀取:
//封裝url請求Model,用於填充到POST請求的body裏
var urlModel = new UrlModel()
{
tenantId = tenantId,
appName = appName,
api_key = ApiKey
};
var url = $"{HOST}/tmlapi/tmlRequest?&tenantId={tenantId}&appName={appName}&api_key={ApiKey}";
//開啓http客戶端發送Post請求
var result = HttpClientHelper.ExecuteFunc<ApiResult<string>>(() => HttpClientHelper.Post<string, UrlModel>(url, urlModel));
在這個過程中我們設計好body裏要放置的POST請求所需參數並將其放置到一個model裏,然後依據配置讀取域名並拼接好url,把應該放置到query裏的參數拼接到url上,連同model一起傳入HttpClient方法中。需要特別注意的是如果是POST請求,那麼定義在body裏的請求參數要放到一個model裏傳進去,而定義爲query的請求參數必須拼接到url上傳入,如果定義爲query,再放到body裏來是會導致缺少參數請求不通的。
總而言之,需要調用HttpClient的時候,一定要封裝一層方法,然後做重試機制的處理,畢竟是訪問站點。還要注意參數的傳遞形式!