c# - 在现有的 HttpClient 中为一个请求设置 AllowAutoRedirect false
问题描述
This answer to the question on how to make HttpClient
not follow redirects 提供了在创建实际客户端时设置的解决方案:
var handler = new HttpClientHandler { AllowAutoRedirect = false };
var client = new HttpClient(handler);
答案下方的评论是我的实际问题:
是否可以在不需要两个单独的 HttpClient 实例(即一个允许重定向,一个不允许重定向)的情况下基于每个请求执行此操作?
我现在想要单独的客户端也有一个特定的原因:我希望客户端从早期请求中保留其 cookie。我正在尝试首先执行一些请求,其中包括有效的重定向,但只有链中的最后一个我不想成为重定向。
我已经搜索并查看了 的重载.GetAsync(url, ...)
,并查看了 的属性和方法HttpClient
,但还没有找到解决方案。
这可能吗?
解决方案
该问题询问是否可以根据具体情况进行以下重定向。虽然对于许多常见情况肯定有用,但我发现现有的答案在这方面缺乏。
以下实现允许通过谓词根据具体情况来决定是否遵循重定向。解决方法是重写 HttpClientHandler 的 SendAsync() 方法。
using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace HttpClientCustomRedirectBehavior
{
static class Program
{
private const string REDIRECTING_URL = "http://stackoverflow.com/";
static async Task Main(string[] args)
{
HttpMessageHandler followRedirectAlwaysHandler = new RestrictedRedirectFollowingHttpClientHandler(
response => true);
HttpMessageHandler followRedirectOnlyToSpecificHostHandler = new RestrictedRedirectFollowingHttpClientHandler(
response => response.Headers.Location.Host == "example.com");
HttpResponseMessage response;
using (HttpClient followRedirectAlwaysHttpClient = new HttpClient(followRedirectAlwaysHandler))
{
response = await followRedirectAlwaysHttpClient.GetAsync(REDIRECTING_URL);
Console.WriteLine(response.StatusCode); // OK
}
using (HttpClient followRedirectOnlyToSpecificHostHttpClient = new HttpClient(followRedirectOnlyToSpecificHostHandler))
{
response = await followRedirectOnlyToSpecificHostHttpClient.GetAsync(REDIRECTING_URL);
Console.WriteLine(response.StatusCode); // Moved
}
followRedirectOnlyToSpecificHostHandler = new RestrictedRedirectFollowingHttpClientHandler(
response => response.Headers.Location.Host == "stackoverflow.com");
using (HttpClient followRedirectOnlyToSpecificHostHttpClient = new HttpClient(followRedirectOnlyToSpecificHostHandler))
{
response = await followRedirectOnlyToSpecificHostHttpClient.GetAsync(REDIRECTING_URL);
Console.WriteLine(response.StatusCode); // OK
}
}
}
public class RestrictedRedirectFollowingHttpClientHandler : HttpClientHandler
{
private static readonly HttpStatusCode[] redirectStatusCodes = new[] {
HttpStatusCode.Moved,
HttpStatusCode.Redirect,
HttpStatusCode.RedirectMethod,
HttpStatusCode.TemporaryRedirect,
HttpStatusCode.PermanentRedirect
};
private readonly Predicate<HttpResponseMessage> isRedirectAllowed;
public override bool SupportsRedirectConfiguration { get; }
public RestrictedRedirectFollowingHttpClientHandler(Predicate<HttpResponseMessage> isRedirectAllowed)
{
AllowAutoRedirect = false;
SupportsRedirectConfiguration = false;
this.isRedirectAllowed = response => {
return Array.BinarySearch(redirectStatusCodes, response.StatusCode) >= 0
&& isRedirectAllowed.Invoke(response);
};
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
int redirectCount = 0;
HttpResponseMessage response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
while (isRedirectAllowed.Invoke(response)
&& (response.Headers.Location != request.RequestUri || response.StatusCode == HttpStatusCode.RedirectMethod && request.Method != HttpMethod.Get)
&& redirectCount < this.MaxAutomaticRedirections)
{
if (response.StatusCode == HttpStatusCode.RedirectMethod)
{
request.Method = HttpMethod.Get;
}
request.RequestUri = response.Headers.Location;
response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
++redirectCount;
}
return response;
}
}
}
Main 方法显示了对http://stackoverflow.com的三个示例请求(这是一个重定向到https://stackoverflow.com的 URI ):
- 第一个 GET 请求将遵循重定向,因此我们看到对重定向请求的响应的状态代码OK ,因为处理程序配置为遵循所有重定向。
- 第二个 GET 请求不会跟随重定向,因此我们会看到状态代码Moved,因为处理程序配置为仅跟随重定向到主机 example.com。
- 第三个 GET 请求将跟随重定向,因此我们看到对重定向请求的响应的状态代码OK ,因为处理程序配置为仅跟随重定向到主机 stackoverflow.com。
当然,您可以用任何自定义逻辑替换谓词。
推荐阅读
- c# - 存储在会话包装器中的列表保留为 Count 0,尽管调用了将项目添加到其中的方法
- azure - “删除 Databricks 作业”是否会立即停止集群上的代码执行?
- python - 如何通过比较熊猫中的多列来找到具有最大值的列名
- webpack - 将 Angular 4 管理面板升级到 Angular 7
- java - Schema-validation: missing table [...] 错误消息使用 flyway 用于 Hibernate 生成的 SQL 代码
- java - 是否可以从 java 创建一个新的 PDB?
- javascript - 在 react-native 中重新访问屏幕时如何重新运行方法?
- javascript - 在数组中查找重复值及其中的间隙
- android - 如何使用 glide* 使用数组列表中的自定义适配器将图像设置为列表视图
- angular - 如何在 Angular 谷歌地图上清除方向