.NET Core 3.1中WebClient不跟随307重定向问题咨询
WebClient不跟随307重定向的原因及解决方案
一、为什么AllowAutoRedirect=true仍不处理307?
这其实是.NET框架的默认设计:虽然WebClient底层依赖的HttpWebRequest默认把AllowAutoRedirect设为true,但它不会自动处理307(临时重定向)和308(永久重定向)这两个状态码。
原因在于HTTP规范对这两个状态码的要求非常严格——它们强制重定向时必须完全保留原始请求的方法(比如GET/POST)和请求体内容。框架为了避免潜在的安全风险(比如自动重复POST请求可能导致重复提交数据),默认把这两个状态码排除在了自动重定向的处理范围之外,哪怕你显式设置AllowAutoRedirect=true也不会生效。
二、如何实现支持307重定向的文件下载?
这里提供两种方案,你可以根据自己使用的.NET版本选择:
方案1:自定义WebClient(适配.NET Framework老版本)
继承WebClient并重写相关方法,手动捕获307响应并发起新请求,还能添加重定向次数限制避免死循环:
public class RedirectAwareWebClient : WebClient { // 限制最大重定向次数,防止无限循环 private readonly int _maxRedirects = 5; private int _currentRedirectCount = 0; protected override WebRequest GetWebRequest(Uri address) { var request = base.GetWebRequest(address) as HttpWebRequest; if (request != null) { // 关闭框架自动重定向,我们手动处理 request.AllowAutoRedirect = false; } return request; } protected override WebResponse GetWebResponse(WebRequest request) { var response = base.GetWebResponse(request) as HttpWebResponse; if (response != null && (response.StatusCode == HttpStatusCode.TemporaryRedirect || response.StatusCode == (HttpStatusCode)308) && // 部分.NET版本未定义308,直接用数值 _currentRedirectCount < _maxRedirects) { _currentRedirectCount++; // 获取重定向目标地址 var redirectUri = new Uri(response.Headers["Location"]); // 复制原始请求的方法和头部信息 var newRequest = (HttpWebRequest)WebRequest.Create(redirectUri); newRequest.Method = request.Method; foreach (var headerKey in request.Headers.AllKeys) { newRequest.Headers[headerKey] = request.Headers[headerKey]; } // 递归处理新的请求 return GetWebResponse(newRequest); } // 重置计数,方便重复使用该客户端实例 _currentRedirectCount = 0; return response; } }
使用方式和原生WebClient完全一致:
using (var client = new RedirectAwareWebClient()) { client.DownloadFile("https://api.spiget.org/v2/resources/10905/download", "your-target-file.zip"); }
方案2:使用HttpClient(推荐,适用于.NET Core/.NET 5+)
HttpClient对307/308的支持更完善,默认就能自动跟随这类重定向(对于GET这种安全请求,框架会自动重复发起请求),代码也更简洁:
// 异步版本(推荐,不阻塞主线程) using (var httpClient = new HttpClient()) { using (var stream = await httpClient.GetStreamAsync("https://api.spiget.org/v2/resources/10905/download")) { using (var fileStream = new FileStream("your-target-file.zip", FileMode.Create, FileAccess.Write)) { await stream.CopyToAsync(fileStream); } } } // 同步版本(如果业务需要同步执行) using (var httpClient = new HttpClient()) { using (var stream = httpClient.GetStream("https://api.spiget.org/v2/resources/10905/download")) { using (var fileStream = new FileStream("your-target-file.zip", FileMode.Create, FileAccess.Write)) { stream.CopyTo(fileStream); } } }
小提示
- 如果你使用的是.NET Framework 4.5及以下版本,
HttpClient对307的支持有限,优先选择自定义WebClient的方案。 - 不管用哪种方案,都建议添加重定向次数限制,避免遇到恶意循环重定向导致程序卡死。
内容的提问来源于stack exchange,提问作者Christian Kern




