content-encoding: utf-8🤔

Content-Encoding 是一个实体消息首部,用于对特定媒体类型的数据进行压缩。当这个首部出现的时候,它的值表示消息主体进行了何种方式的内容编码转换。这个消息首部用来告知客户端应该怎样解码才能获取在 Content-Type 中标示的媒体类型内容

今天有个同事对接第三方支付时,遇到一个很有趣的问题:

『对于第三方返回的支付回调信息,系统无法解析。提示错误 Error: Unsupported Content-Encoding: utf-8


第一反应是没有按第三方要求的格式提供回调接口,可一看对方需求:

业务方提供 post application/json 的回调地址


这太常用了,系统完全支持,问题肯定不在这。结合报错,然后看了下收到消息头,很有趣的是发现 header 中塞入了: Content-Encoding: utf-8

MDN 上关于 Content-Encoding 的解释如引文所说,这是标识媒体编码方式的,可选指令包括:

  • gzip
  • compress
  • deflate
  • identity
  • br

从定义也能看出,utf-8 放在这里是不合适的。然后求教了一下大佬,github、stackoverflow 找了一圈,确认这样做是无意义的,并且理论上就是会返回 415 状态。然后同事带着满满自信去找对方反馈,对方很自信地贴出了头部设置代码:

1
2
se.setContentEncoding("utf-8");
httpPost.addHeader("Content-Type", "application/json;encoding=utf-8");

application/json;encoding=utf-8 是什么?是想表达 content-type: application/json; charset=utf-8 的意思吗? 然后因为对方服务器的稳定性,需要我们这边做处理,向渠道大佬低头…

简单说说同事最后的处理:主要问题还是 koa-bodyparser 中间件处理消息时,其中引用 co-body 库发现头部 content-encoding 值为 utf-8 自然抛出了 415 状态,简单贴一下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
switch (encoding) {
  case 'gzip':
  case 'deflate':
    break
  case 'identity':
    return stream
  default:
    var err = new Error('Unsupported Content-Encoding: ' + encoding)
    err.status = 415
    throw err
}

看到有人在使用 express 时也有过类似问题,并且提过 pr 给底层库,但是作者回应这并不属于 bug(这确实是个正经逻辑🤔),那么有两种简单的解决办法:

  1. 修改底层依赖库代码,上面贴的头部判断部分对 utf-8 直接返回数据
  2. 增加全局中间件,对每一次请求去掉头部 Content-Encoding 参数,再丢给后面中间件解析消息

虽然不是很麻烦的改动,却是很妥协的处理。最后: