You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 4.8 HttpClient调用服务抛ArgumentException:仅允许http/https协议

解决.NET Framework 4.8 HttpClient无法处理非HTTP/HTTPS协议响应头的问题

问题根源

.NET Framework 4.8的HttpClient依赖底层HttpWebRequest实现,当响应中包含Content-Location这类需要解析为Uri的头时,HttpWebRequest会严格校验协议类型,只允许http/https,一旦遇到resource://这类非标准协议,就会抛出ArgumentException。而.NET Core/.NET 8重构了HttpClient的底层逻辑,对非标准协议头的处理更宽松,不会强制解析为Uri,因此不会触发异常。

另外,异常发生在自定义HttpMessageHandler执行之前——因为默认的handler会先完成响应头的解析工作,所以自定义处理器无法在异常抛出前拦截修改响应头。

解决方案

方案1:直接使用HttpWebRequest手动处理响应

绕过HttpClient的默认解析逻辑,直接用HttpWebRequest发送请求,手动读取并过滤/修改有问题的响应头:

var request = (HttpWebRequest)WebRequest.Create("https://example.com/path/to/resource/123");
request.Method = "GET";

using (var response = (HttpWebResponse)request.GetResponse())
{
    // 手动遍历响应头,跳过非法的Content-Location
    foreach (string headerName in response.Headers)
    {
        if (!headerName.Equals("Content-Location", StringComparison.OrdinalIgnoreCase))
        {
            // 处理合法的响应头
            Console.WriteLine($"{headerName}: {response.Headers[headerName]}");
        }
    }

    // 读取响应内容
    using (var stream = response.GetResponseStream())
    using (var reader = new StreamReader(stream))
    {
        var content = reader.ReadToEnd();
        // 后续业务逻辑
    }
}

方案2:自定义HttpMessageHandler替换底层实现

如果需要保留HttpClient的调用方式,可以自定义一个HttpMessageHandler,内部用HttpWebRequest处理请求和响应,手动过滤非法响应头后再封装为HttpResponseMessage:

public class CustomHttpHandler : HttpMessageHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var webRequest = (HttpWebRequest)WebRequest.Create(request.RequestUri);
        webRequest.Method = request.Method.Method;

        // 复制请求头(处理受限头)
        foreach (var header in request.Headers)
        {
            if (WebHeaderCollection.IsRestricted(header.Key))
            {
                switch (header.Key.ToLower())
                {
                    case "accept":
                        webRequest.Accept = string.Join(", ", header.Value);
                        break;
                    case "content-type":
                        webRequest.ContentType = string.Join(", ", header.Value);
                        break;
                    // 按需处理其他受限请求头
                }
            }
            else
            {
                webRequest.Headers.Add(header.Key, string.Join(", ", header.Value));
            }
        }

        // 处理请求内容(POST/PUT等场景)
        if (request.Content != null)
        {
            var contentBytes = await request.Content.ReadAsByteArrayAsync(cancellationToken);
            webRequest.ContentLength = contentBytes.Length;
            using (var stream = await webRequest.GetRequestStreamAsync())
            {
                await stream.WriteAsync(contentBytes, 0, contentBytes.Length, cancellationToken);
            }
        }

        // 获取响应(处理错误状态码)
        HttpWebResponse webResponse;
        try
        {
            webResponse = (HttpWebResponse)await webRequest.GetResponseAsync();
        }
        catch (WebException ex) when (ex.Response is HttpWebResponse)
        {
            webResponse = (HttpWebResponse)ex.Response;
        }

        // 构建HttpResponseMessage,过滤非法响应头
        var responseMessage = new HttpResponseMessage((HttpStatusCode)webResponse.StatusCode);
        var content = new StreamContent(webResponse.GetResponseStream());

        // 复制通用响应头
        foreach (string headerName in webResponse.Headers)
        {
            if (!headerName.Equals("Content-Location", StringComparison.OrdinalIgnoreCase))
            {
                responseMessage.Headers.TryAddWithoutValidation(headerName, webResponse.Headers[headerName]);
            }
        }

        // 复制内容响应头
        foreach (string headerName in webResponse.ContentHeaders)
        {
            if (!headerName.Equals("Content-Location", StringComparison.OrdinalIgnoreCase))
            {
                content.Headers.TryAddWithoutValidation(headerName, webResponse.ContentHeaders[headerName]);
            }
        }

        responseMessage.Content = content;
        return responseMessage;
    }
}

使用自定义handler创建HttpClient:

var client = new HttpClient(new CustomHttpHandler());
var response = await client.GetAsync("https://example.com/path/to/resource/123");
// 后续处理响应

结论

.NET Framework 4.8的默认HttpClient确实无法直接处理包含非HTTP/HTTPS协议的响应头,必须通过上述自定义底层处理逻辑的方式绕过协议校验。

内容的提问来源于stack exchange,提问作者Greg Sandstrom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 03:50:09