zoukankan      html  css  js  c++  java
  • 我又踩坑了!如何为HttpClient请求设置Content-Type?

    1. 坑位

    最近在重构认证代码,认证过程相当常规:

    POST   /open-api/v1/user-info?client_id&timstamp&rd=12345&sign=***&method=hmac
    content-type: application/json
    payload: { "token":"AA2917B0-C23D-40AB-A43A-4C4B61CC7C74"}
    

    平台显示 :签名校验失败, 排查到平台收到的Post Payload并非预期,阅读本文,解锁正确使用Content-Type标头的姿势。

    2. 步步为营

    下面是构造HttpClient对象、发起请求的代码:

    // 初始化HttpClientFactory
     context.Services.AddHttpClient("platform", c =>
    {
        c.BaseAddress = new Uri("https://alpha-engage.demohost.com/");
        c.DefaultRequestHeaders.Accept
        .Add(new MediaTypeWithQualityHeaderValue("application/json"));
    })...
    
    // 产生命名HttpClient,发起请求
     var client = _clientFactory.CreateClient("platform");
     var response = await client.PostAsync($"open-api/v1/user-token/info?{req.AuthString()}",new StringContent(req.ReqPayload.ToString(),Encoding.UTF8) );
    

    平台日志显示,收到的请求payload:

    {"token":"AA2917B0-C23D-40AB-A43A-4C4B61CC7C74"}
    

    额,平台收到的JSON数据被转码了,没有识别出JSON?
    明眼人一看,HttpClient请求没有设置Content-Type,接收端没有识别出payload是JSON,接收时进行了转码,生成了错误签名。

    ① Content-Type是一个Entity Header,指示资源的media type ,可用在请求或者响应中。
    ② 以上代码中new StringContent(req.ReqPayload.ToString(),Encoding.UTF8)指定了Encoding=UTF-8,却没有指定mediaType,源码默认值:text/plain


    当我尝试添加Content-Type时(下面黄色背景行代码):

     context.Services.AddHttpClient("platform", c =>
    {
        c.BaseAddress = new Uri("https://alpha-engage.demohost.com/");
        c.DefaultRequestHeaders.Accept
                    .Add(new MediaTypeWithQualityHeaderValue("application/json"));//ACCEPT header
    
        c.DefaultRequestHeaders.Add("content-type", "application/json");
    })
    

    此时抛出以下异常:

    InvalidOperationException: Misused header name. Make sure request headers are used with
    HttpRequestMessage, response headers with HttpResponseMessage, and
    content headers with HttpContent objects. 
    

    纳尼,HttpContent Headers是啥?Chrome dev tools显示只有两种Header啊?

    3. 爬坑

    官方资料显示: HTTP Headers被分为如下四类:

    --- 信息 举例 对应.NET类型
    General Header 可同时作用在请求/响应中,但是与传输数据无关 Upgrade、Connection ---
    Request Header 将要获取的资源或客户端本身的信息 Accept、Authorization System.Net.Http.Headers.HttpRequestHeaders
    Response Header 响应信息 Location、ETag System.Net.Http.Headers.HttpResponseHeaders
    Entity Header 实体Body额外的信息 Content-Length、Connection System.Net.Http.Headers.HttpContentHeaders

    Content-Type属于Entity Header中的一种。

    即便Entity Header既不是请求标头也不是响应标头,它们还是会包含在请求/响应标头术语中(此说法来自官方)。

    所以我们在Chrome DevTools没有看到Entity Headers分组,却常在请求/响应标头中看到Content-Type标头。

    回到上面的异常,.NET 严格区分四种标头,所以我上面
    c.DefaultRequestHeaders.Add("content-type", "application/json");尝试在请求头添加Content-Type标头, 姿势错误, .NET会提示InvalidOperationException异常。

    4. 填坑

    给这个常规的Post请求 设置正确的Content-Type,

    ① 对HttpRequestMessage对象Content属性添加Header

     using (var request = new HttpRequestMessage())
    {
         request.Method = new HttpMethod(method);
         request.RequestUri = new Uri(url);
         request.Content = new StringContent(payload);
         request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
         var response = await _httpClient.SendAsync(request);
         return response;
    }
    

    使用HttpClient.SendAsync(request)

    ② 对StringContent重载构造函数传参

    StringContent某个重载构造函数可直接传参,设置media type

    var response = await client.PostAsync($"open-api/v1/user-token/info?{req.AuthString()}",new StringContent(req.ReqPayload.ToString(),Encoding.UTF8,"application/json") );
    

    干货旁白

    1. 小编对于Http协议有知识漏洞,搬砖时一直关注Chrome DevTools,忽略了还有Entity Header一说。
    2. Content-Type 这个实体标头,会出现了请求/响应标头,指示资源的媒体类型。
    3. .NTE针对4种HTTP Header强化了区别,在实际开发中要灵活使用。
  • 相关阅读:
    BootStrap练习
    表单控件练习
    K近邻算法原理
    CSS 边框和颜色
    SVG平移和旋转
    SVG进阶练习
    SVG路标(marker)
    SVG 曲线与文字
    python函数与异常处理
    if-elif-else分支判断语句(附加continue和break)---举例说明
  • 原文地址:https://www.cnblogs.com/JulianHuang/p/13746963.html
Copyright © 2011-2022 走看看