HttpClient 禁用自动重定向

Intro

前段时间写了一个小工具来帮助我们简化一个每个月一次的小任务,每个月我们公司的 BI Team 会给我们上个月访问量比较高的博客文章的 url,然后我们会根据 BI 提供博客的 url 去找到对应的博客 id,然后更新到配置中,网站会读取这个配置来显示比较受欢迎的博客文章

Redirection

我们的博客文章有一个特点,如果访问的地址只有博客文章的路径,会自动跳转到带 id 的地址,比如你访问:

https://cn.iherb.com/blog/8-natural-remedies-for-heartburn-and-acid-reflux 会自动跳转到 https://cn.iherb.com/blog/8-natural-remedies-for-heartburn-and-acid-reflux/347 ,后面新增的 347 就是对应的博客文章的 id,BI team 的同事给到我们的就是示例中的 8-natural-remedies-for-heartburn-and-acid-reflux

就是服务器端做了一个重定向,通过 浏览器的 Network 我们可以看到发生了一个 302 重定向

图片

HttpClient AutoRedirect

根据上面的重定向,我们使用代码是不是也可以做呢,这样我们就可以做到比较自动化了,来尝试一下

var articlePath = "/8-natural-remedies-for-heartburn-and-acid-reflux/";
var httpClient = new HttpClient()
{
    BaseAddress = new Uri("https://cn.iherb.com/blog/")
};
using var res = await httpClient.GetAsync(articlePath[0].TrimStart('/'));
Console.WriteLine(res.RequestMessage.RequestUri.ToString());
Console.WriteLine(res.StatusCode);

猜一猜上面的代码输出结果是什么?

输出结果如下:

图片

和你预想的结果一样吗,可以看到输出的请求地址实际上并不是我们请求的地址,这是因为默认地,HttpClient 会自动跟随重定向,如果服务器重定向了一个请求,HttpClient 会读取 response header 中的 Location 来请求下一个地址,请求地址和 response 显示的是最终一次请求的信息。

你也可以选择禁用自动重定向,下面就是一个禁用重定向的示例:

using var httpClient = new HttpClient(new HttpClientHandler()
{
    AllowAutoRedirect = false
})
{
    BaseAddress = new Uri(BaseUrl)
};
using var res1 = await httpClient.GetAsync(articlePath[0].TrimStart('/'));
Console.WriteLine(res1.RequestMessage.RequestUri.ToString());
Console.WriteLine(res1.StatusCode);

我们可以通过指定 HttpClientHandlerAllowAutoRedirect 属性来禁用自动重定向,禁用后的输出结果如下:

图片

可以看到这个请求的请求地址是我们实际请求的地址,没有发生重定向

那么针对哪些请求会做重定向处理呢?通过查阅源码发现对于这些响应会尝试重定向 300/301/302/307/308, 可以参考:https://github.com/dotnet/runtime/blob/74246130d505c9243396f4a8837634e8ab3065bb/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RedirectHandler.cs#L87

图片

Practice

我们已经知道 HttpClient 会自动重定向,而且我们只需要拿到重定向后的地址即可,那我们就可以禁用自动重定向,直接读取 Response Header 里的 Location 中的值,就可以拿到要重定向的 URL 了,于是就有了下面的代码来获取博客文章的 id

private async Task<intGetArticleId(string path)
{
  using var response = await httpClient.GetAsync(path.TrimStart('/'));
  var statusCode = (int)response.StatusCode;
  if(statusCode != 302)
  {
      return -1;
  }
  var newLocation = response.Headers.Location.ToString();
  int.TryParse(newLocation[(newLocation.LastIndexOf('/') + 1)..], out var articleId);
  return articleId;
}

通过上面的代码我们就可以相对高效的拿到博客文章的 id,再进一步整理一下构造成我们最终配置所需要的格式

var articleIds = await Task.WhenAll(articlePath.Select(path => GetArticleId(path)));
var json = new
{
    articlelist = articleIds.Select(x=> new{ id=x })
}.ToJson();

这样以后我们再统计只需要拷贝一下新的数据,跑一下脚本就可以了~~

More

因为我们只关注 Response Header ,所以我们也可以把上面的 GET 请求换成 HEAD 请求,这样返回的响应就只有 Header,没有 Body 更加简洁一些

从业务的角度来看,我们可以尝试让别的 Team 同事直接提供一个 API 来返回这样的数据,这样我们就不必这样搞了 ^^

References

  • https://github.com/WeihanLi/SamplesInPractice/blob/master/HttpClientTest/NoAutoRedirectSample.cs
  • https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/System/Net/Http/HttpHandlerDefaults.cs
  • https://github.com/dotnet/runtime/blob/74246130d505c9243396f4a8837634e8ab3065bb/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RedirectHandler.cs#L87
  • https://stackoverflow.com/questions/14731980/using-httpclient-how-would-i-prevent-automatic-redirects-and-get-original-statu
  • https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
  • https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclienthandler.allowautoredirect?WT.mc_id=DT-MVP-5004222&view=net-5.0
  • https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclienthandler.maxautomaticredirections?view=net-5.0&WT.mc_id=DT-MVP-5004222


来源:https://mp.weixin.qq.com/s/OtKP6WM1xXB4gB42F8lnyQ
点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信小程序

微信扫一扫体验

立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部