C# 踩坑记录之 如何为 HttpClient 添加Header请求头

管理员

前提

使用HttpClient进行post请求 请求参数包含自定义header和body

原错误代码
//headers 为 Dictionary<string, string> headers 用户自定义参数
 var client = new HttpClient(httpClientHandler);
                client.Timeout = new TimeSpan(0, 0, timeout); ;
                StringContent stringContent = new StringContent(parameter, Encoding.UTF8, HeaderConsts.JsonContentType);

                if (headers != null && headers.Count > 0)
                {
                    foreach (var header in headers)
                    {
                        if (string.IsNullOrWhiteSpace(header.Key) || string.IsNullOrWhiteSpace(header.Value))
                        {
                            TaskLogHelper.LogError($"Headers存在为空,url-{url},headers-{JsonConvert.SerializeObject(header)}", segmentId);
                            continue;
                        }
                        if (header.Key == HeaderConsts.Host)
                        {
                            var matchs = Regex.Matches(url, "([^/:]+)");
                            client.DefaultRequestHeaders.Host = matchs[1].Value;//请求头添加
                        }
                        //【主要错误代码】
                        else
                           stringContent.Headers.TryAddWithoutValidation(header.Key, header.Value); //错误点  headers包含除host的请求头、响应头  这里将header尝试加到**内容头** 添加失败 且未做日志 造成请求头缺失 请求失败
                    }
                }

                return (await (await client.PostAsync(url, stringContent)).Content.ReadAsStringAsync(), string.Empty);
后将错误代码行调整为以下代码 仍存在问题
  //stringContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
  client.DefaultRequestHeaders.Add(header.Key, header.Value);//headers包含响应头

报错:Misused header name. Make sure request headers are used with HttpRequestMessage, response

分析:

https://learn.microsoft.com/zh-cn/dotnet/api/system.net.http.headers.httpheaders.add?view=net-7.0
Header分三种类型 请求头 内容头 响应头【具体包含见最下方】

优化代码 问题解决
//headers 为 Dictionary<string, string> headers 用户自定义参数
     var client = new HttpClient(httpClientHandler);
                client.Timeout = new TimeSpan(0, 0, timeout);
               
                StringContent stringContent = new StringContent(parameter, Encoding.UTF8, "application/json");//创建请求内容 并指定内容的编码格式和content-type
                if (headers != null && headers.Count > 0)
                {
                    foreach (var header in headers)
                    {
                        if (string.IsNullOrWhiteSpace(header.Value))
                        {
                            TaskLogHelper.LogError($"Headers存在为空,url-{url},headers-{JsonConvert.SerializeObject(header)}", segmentId);
                            continue;
                        }
                        if (header.Key == HeaderConsts.Host)
                        {
                            var matchs = Regex.Matches(url, "([^/:]+)");
                            client.DefaultRequestHeaders.Host = matchs[1].Value;//host要通过正则获取纯域名
                        }
                        else
                        {
                            //【主要修改部分】
                            if (!client.DefaultRequestHeaders.TryAddWithoutValidation(header.Key, header.Value))//尝试添加为请求头 
                            {
                                 if (!stringContent.Headers.TryAddWithoutValidation(header.Key, header.Value))//尝试添加为内容头
                                {
                                    TaskLogHelper.LogDebug($"Headers插入失败--{header.Key}:{header.Value},url-{url}");//包含响应头 日志提示
                                    continue;
                                }
                            }
                            
                        }
                        
                    }
                }

                return (await (await client.PostAsync(url, stringContent)).Content.ReadAsStringAsync(), string.Empty);

请求头 内容头 响应头区别

请求头

Accept: 览器支持媒体类型, 比如 text/html,application/json,image/webp,/...
Accept-Encoding: 声明浏览器支持的编码类型,gzip, deflate
Accept-Language: 客户端接受的语言格式,比如 zh-CN
Connection: 请求连接状态 Eg:keep-alive 持久连接
Host:服务器的域名
Origin:告诉服务器请求从哪里发起的,仅包括协议和域名 CORS跨域请求中可以看到response有对应的header,Access-Control-Allow-Origin
Referer:告诉服务器请求的原始资源的URI,其用于所有类型的请求,并且包括:协议+域名+查询参数; 很多抢购服务会用这个做限制,必须通过某个入来进来才有效
User-Agent: 服务器通过这个请求头判断用户的软件的应用类型、操作系统、软件开发商以及版本号、浏览器内核信息等; 风控系统、反作弊系统、反爬虫系统等基本会采集这类信息做参考
Cookie: 表示服务端给客户端传的http请求状态,也是多个key=value形式组合,比如登录后的令牌等
Content-Type: HTTP请求提交的内容类型,一般只有post提交时才需要设置,比如文件上传,表单提交等

响应头

llow: 服务器支持哪些请求方法
Content-Length: 响应体的字节长度
Content-Type: 响应体的MIME类型
Content-Encoding: 设置数据使用的编码类型
Date: 设置消息发送的日期和时间
Expires: 设置响应体的过期时间,一个GMT时间,表示该缓存的有效时间
cache-control: Expires的作用一致,都是指明当前资源的有效期, 控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据,优先级高于Expires,控制粒度更细,如max-age=240,即4分钟
Location:表示客户应当到哪里去获取资源,一般同时设置状态代码为3xx
Server: 服务器名称
Transfer-Encoding:chunked 表示输出的内容长度不能确定,静态网页一般没,基本出现在动态网页里面
Access-Control-Allow-Origin: 定哪些站点可以参与跨站资源共享

内容头

其实就是 Content-type 常在创建内容时即可指定

new StringContent(parameter, Encoding.UTF8, "application/json");//第三个参数即Content-type  
  • Content-type 有多种参数
    text/html :HTML格式

text/plain :纯文本格式

text/xml : XML格式

image/gif :gif图片格式

image/jpeg :jpg图片格式

image/png:png图片格式

application/json:JSON数据格式

application/pdf :pdf格式

application/octet-stream :二进制流数据,一般是文件下载

application/x-www-form-urlencoded:form表单默认的提交数据的格式,会编码成key=value格式

multipart/form-data: 表单中需要上传文件的文件格式类型