从wireshark的评论来看,python-requests 似乎做错了,但可能没有“正确的答案”。
RFC 2388 says
最初采用非 ASCII 字符集的字段名称可以使用 RFC 2047 中描述的标准方法在“name”参数的值内进行编码。
RFC 2047,反过来说
一般来说,“编码字”是一系列可打印的 ASCII 字符,以“=?”开头,以“?=”结尾,中间有两个“?”。它指定了字符集和编码方法,还包括根据该编码方法的规则编码为图形 ASCII 字符的原始文本。
并继续描述“Q”和“B”编码方法。使用“Q”(引用打印)方法,名称将是:
=?utf-8?q?=E2=98=83?=
BUT, as RFC 6266明确指出:
“编码字”不得用在 MIME Content-Type 或 Content-Disposition 字段的参数中,也不能用在除“comment”或“phrase”之外的任何结构化字段主体中。
所以我们不可以这样做。 (感谢@Lukasa 的这次捕获!)
RFC 2388 还说
也可以提供原始本地文件名,或者作为
“文件名”参数“内容处置:表单数据”之一
标头,或者在多个文件的情况下,在“内容处置:
file”子部分的标头。发送应用程序可以提供
文件名;如果发件人操作系统的文件名不是
在 US-ASCII 中,文件名可能是近似的,或者使用
RFC 2231 的方法。
And RFC 2231描述了一种看起来更像您所看到的方法。在里面,
星号(“*”)被重复使用来指示语言和
存在字符集信息并且正在使用编码。 A
单引号(“'”)用于分隔字符集和语言
参数值开头的信息。百分号
(“%”)用作编码标志,符合RFC 2047。
具体来说,参数名称末尾的星号充当
指示字符集和语言信息可能出现在
参数值的开头。单引号用于
将字符集、语言和实际值信息分开
参数值字符串,百分号用于标记
以十六进制编码的八位位组。
也就是说,如果采用此方法(并且两端都支持),则名称应为:
name*=utf-8''%E2%98%83
幸运的是,RFC 5987添加编码基于 RFC 2231到 HTTP 标头! (感谢 @bobince 的发现)它说你可以(任何可能应该)包含 RFC 2231 样式的值and一个简单的值:
头域规范需要定义是否多个实例
允许具有相同 parmname 组件的参数,以及如何
他们应该被处理。该规范建议
使用扩展语法的参数优先。这个会
允许制作者使用两种格式而不破坏接收者
还不明白扩展语法。
Example:
foo:酒吧; title="欧元汇率";
标题*=utf-8''%e2%82%ac%20exchange%20rates
然而,在他们的例子中,他们“简化”了“遗留客户”的简单价值。这实际上并不是表单字段名称的选项,所以看起来像最好的方法可能是将两者都包括在内 name=
and name*=
版本,其中纯值(如 @bobince 所描述)“仅以与表单相同的编码发送带引号的字节”,例如:
Content-Disposition: form-data; name="☃"; name*=utf-8''%E2%98%83
也可以看看:
- Java 中的 HTTP 标头编码/解码
- 如何根据 RFC 2231 对文件名进行编码?
- 如何在 HTTP 中对 Content-Disposition 标头的文件名参数进行编码?
最后,看到http://larry.masinter.net/1307multipart-form-data.pdf (also https://www.w3.org/Bugs/Public/show_bug.cgi?id=16909#c8),其中建议通过坚持使用 ASCII 表单字段名称来避免该问题。