一个由 Content-Length 与 Content-Type 引发的问题
原文链接 http://judes.me/tech/2018/05/17/content_length_and_content_type.html
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。
问题
在之前介绍 fiddler 使用技巧的文章中,我提到伪造 json 返回响应时,要设置正确的 Content-Length 。
规范定义了在大多数情况要设置 Content-Length ,只有少数情况例外。在浏览器实现上,如果设置了比正确数量要少的 Content-Length ,那么内容会被截断,若比正确数量要多,那么会一直处在等待加载更多内容的状态。
之前也提到过,如果伪造的 json 中只有英文和数字,选中它们后,编辑器上显示选中多少字符, Content-Length 就设为多少。如果 json 中还有中文,那么一个中文字符就要当成三个英文字母来计算。
为什么是这样?
答案
首先从规范中摘取 Content-Length 部分定义:
The Content-Length entity-header field indicates the size of the entity-body, in decimal number of OCTETs...
上面的 OCTET 指的是 , 任意连续的八比特数据,可以近似理解为一个字节。
也就是说 Content-Length 指的是响应 body 的字节长度,而不是字符串长度。
同一个字符,在不同的编码规则下,有可能会有不同的字节长度。比如下面展示的大写字母 "A" 的情况:
字符 | ASCII | UTF-16 | UTF-8 |
---|---|---|---|
A | 01000001 | 00000000 01000001 | 01000001 |
正因如此,服务器应该在设置 Content-Length 的同时,也设置字符编码 charset 。根据这里所说,如果不设置,在 HTTP 1.1 中 charset 默认为 ISO-8859-1 。
在响应头的 Content-Type 字段,可以设置 charset ,通用的做法是设置为 UTF-8 。UTF-8 兼容 ASCII 字符集,同时采用变长编码方式,节省网络流量。
在 UTF-8 变长编码方式下,英文和数字用一字节编码,绝大部分汉字用三字节编码。这就是为什么一个中文字符要当成三个英文字母。
工具
如果你很不幸像我一样要经常计算带有中文的 Content-Length ,可以尝试用 javascript 代码帮你计算字符串的字节长度:
//https://stackoverflow.com/questions/5515869/string-length-in-bytes-in-javascript
function byteLength(str) {
// returns the byte length of an UTF-8 string
var s = str.length;
for (var i=str.length-1; i>=0; i--) {
var code = str.charCodeAt(i);
if (code > 0x7f && code <= 0x7ff) s++;
else if (code > 0x7ff && code <= 0xffff) s+=2;
if (code >= 0xDC00 && code <= 0xDFFF) i--; //trail surrogate
}
return s;
}
javascript 用的是 Unicode 字符集,上面的代码用 charCodeAt 获取字符的 Unicode 编码值,然后根据值所在的区间和 Unicode 与 UTF-8 的对应关系,推算字节数。
更多参考