最近在做的一个功能是通过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的时候,一定要封装一层方法,然后做重试机制的处理,毕竟是访问站点。还要注意参数的传递形式!