以前是写前端的,感觉前端的类似axios的拦截器很方便,可以在请求的发送前后做一些通用化的操作,现在以为工作原因刚刚接触.NET,在.NET中请求库中经过挑选最后选择了HTTPClient这个类库,就在想有没有类似axios拦截库一样的功能,在查了一下官网文档,是推荐在应用程序的入口处就using,这样在整个应用周期中都可以使用HTTPClient。
先贴一下应用入口的代码:
/**
* 使用HttpClinet实例
*/
public static readonly HttpClient httpClient = new HttpClient();
以上代码是最简单的引入HTTPClient请求库,现在贴一下简单版本的DelegatingHandler拦截器代码:
public class CustomDelegatingHandler : DelegatingHandler
{
private readonly TimeSpan _timeout;
public CustomDelegatingHandler(TimeSpan timeout)
{
_timeout = timeout;
}
// 请求前拦截
protected async Task<HttpRequestMessage> InterceptRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Console.WriteLine($"Sending request to {request.RequestUri}");
return request;
}
// 响应后拦截
protected async Task<HttpResponseMessage> InterceptResponseAsync(HttpResponseMessage response, CancellationToken cancellationToken)
{
Console.WriteLine($"Received response with status code {response.StatusCode}");
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
return response;
}
else if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
{
Console.WriteLine("响应返回了状态码 500 - Internal Server Error");
MessageBox.Show("未接收到正确返回信息。", "提示信息", MessageBoxButton.OK, MessageBoxImage.Information);
}
else
{
Console.WriteLine($"响应返回了其他状态码: {(int)response.StatusCode}");
MessageBox.Show($"接收到错误返回信息{(int)response.StatusCode},请联系信息科。", "提示信息", MessageBoxButton.OK, MessageBoxImage.Information);
}
return response;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// 请求前拦截
var interceptedRequest = await InterceptRequestAsync(request, cancellationToken);
// 创建一个链接到原始cancellationToken的新CancellationTokenSource
using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
{
// 设置超时时间(例如30秒)
linkedCts.CancelAfter(TimeSpan.FromSeconds(30));
HttpResponseMessage response = null;
try
{
// 使用linkedCts.Token而不是原始的cancellationToken
response = await base.SendAsync(interceptedRequest, linkedCts.Token);
response.EnsureSuccessStatusCode();
}
catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
{
// 如果超时发生
throw new TimeoutException("The request timed out.");
}
catch (Exception)
{
throw; // 其他异常保持原样抛出
}
// 响应后拦截
return await InterceptResponseAsync(response, linkedCts.Token);
}
}
}
然后再在将应用入口的引入代码更改为:
public static readonly HttpClient httpClient = new HttpClient(new CustomDelegatingHandler(TimeSpan.FromSeconds(10)));
这里有一个非常重要的踩坑点需要注意,在实际运用程序的时候,会在response = await base.SendAsync(interceptedRequest, linkedCts.Token);卡住没有反应,需要将入口处的引入修改成如下所示就能解决。
public static readonly HttpClient httpClient = new HttpClient(new CustomDelegatingHandler(TimeSpan.FromSeconds(10))
{
InnerHandler = new HttpClientHandler()
});
关于为什么会卡住,猜测是因为base.SendAsync
的调用是异步的,但是在拦截器中,无法正确的接收到这个异步方法的完成情况,所以就导致了循环请求的感觉,这个只是我目前的个人猜测,请各位大佬予以指教。