“内容类型:application / json; charset = utf-8”真的是什么意思?


284

当我向REST服务发出带有JSON正文的POST请求时,我将其包含Content-type: application/json; charset=utf-8在消息头中。没有此标头,我会从服务中获取错误。我也可以Content-type: application/json不用;charset=utf-8部分就成功使用。

究竟是charset=utf-8做什么的?我知道它指定了字符编码,但是如果没有它,服务就可以正常工作。这种编码是否限制了邮件正文中可以包含的字符?



8
有趣的是,根据IANA的application/json媒体类型注册charset,尽管实际上经常提供,但似乎根本没有受支持的参数。
Uux

1
I know it specifies the character encoding but the service works fine without it.“工作”并不总是意味着“现有的代码/配置是涵盖所有极端情况而做一件事情的最正确方法”。它取决于所有在其他情况下可能不起作用的约定和假设。就我个人而言,我总是尽量保持露骨。
WesternGun

3
发送“字符集”参数不正确且毫无意义。参见RFC 8259,第11节,最后一句。
朱利安·雷施克

Answers:


282

标头仅表示内容被编码的内容。不一定可以从内容本身推断出内容的类型,即,您不必仅查看内容并知道如何处理。这就是HTTP标头的用途,它们告诉接收者(据说)正在处理哪种内容。

Content-type: application/json; charset=utf-8将内容指定为JSON格式,并以UTF-8字符编码进行编码。指定编码对于JSON有点多余,因为JSON的默认(仅?)编码为UTF-8。因此,在这种情况下,接收服务器显然很高兴知道它正在处理JSON,并假定默认情况下编码为UTF-8,这就是为什么有或没有标头的情况。

这种编码是否限制了邮件正文中可以包含的字符?

否。您可以在标题和正文中发送您想要的任何内容。但是,如果两者不匹配,您可能会得到错误的结果。如果您在标头中指定内容是UTF-8编码的,但实际上是在发送Latin1编码的内容,则接收方可能会产生垃圾数据,试图将Latin1编码的数据解释为UTF-8。当然,如果您指定要发送的是Latin1编码的数据,而实际上是在发送,则可以,您只能使用Latin1编码的256个字符。


4
当然,在JSON中,您仍然可以使用转义序列(例如)来表示非Latin1字符\u20AC
dan04 '02

31
根据json的标准,实际上不允许您使用latin1进行内容编码。JSON内容必须以Unicode编码,可以是UTF-8,UTF-16或UTF-32(大端或小端)。
Daniel Luna 2013年

20
在application / json上没有charset参数。
朱利安·里施克(Julian Reschke)2013年

7
@DanielLuna是正确的,application/json必须采用ucs转换格式之一。此外,由于前四个字节JSON的是有限的,你可以随时知道它是8,16,或32 它的字节序。
杰森·可可

4
如果charset=utf-8出于安全考虑,如果是多余的事件,则可能要包括:github.com/shieldfy/API-Security-Checklist/issues/25
manuc66 '17

142

为了证实@deceze的说法,即默认JSON编码为UTF-8 ...

IETF RFC4627

JSON文本应以Unicode编码。默认编码为UTF-8。

由于JSON文本的前两个字符始终是ASCII字符[RFC0020],因此可以确定八位字节流是UTF-8,UTF-16(BE或LE)还是UTF-32(BE或LE)通过查看前四个八位位组中的空值模式。

      00 00 00 xx  UTF-32BE
      00 xx 00 xx  UTF-16BE
      xx 00 00 00  UTF-32LE
      xx 00 xx 00  UTF-16LE
      xx xx xx xx  UTF-8

11
将JSON视为二进制格式而不是文本格式总是有帮助的。
苏珊(Sulthan)2015年

2
既然RFC7159已淘汰了RFC4627,RFC7159指出根值可能是字符串(与以前的规范形成鲜明对比),那么现在如何实现呢?规范在这方面含糊不清,只是说允许使用三种编码,但不允许使用哪种编码来区分它们。
Fabio Beltramini 2015年

4
@FabioBeltramini上面的内容仍然适用,因为JSON中的字符串将不包含任何文字空字符(JSON中的空值需要使用数字转义序列ie进行编码"\u0000")。
thomasrutter 2015年

3
实际上,在这种情况下,UTF-16xx中的第二个字符可能不会为NULL,但是仍然可以从其他字节中确定编码:xx 00 00 00仍然是UTF-32LE,xx 00 xx xx仍然是UTF-16LE,00 xx xx xx仍然是UTF-16BE。
thomasrutter

19

注意,IETF RFC4627已被IETF RFC7158取代。在第[8.1]节中,它撤回了@Drew先前引用的文本,说:

Implementations MUST NOT add a byte order mark to the beginning of a JSON text.

不过,该假设仍然成立,因为任何有效的json仍将以两个ascii字符开头。
Larsing

一个字符,因为一个数字是有效的JSON文件
Nayuki

0

我完全同意@deceze,但我想提出问题的“我从服务中得到错误”部分,

我们收到的这类错误为http 415

Http 415不支持的媒体类型错误

HTTP 415不受支持的媒体类型客户端错误响应代码指示服务器拒绝接受请求,因为有效负载格式为不受支持的格式。

格式问题可能是由于请求指示的Content-TypeContent-Encoding导致的,或者是直接检查数据的结果。

换句话说,例如在https://stackoverflow.com/a/22643964/914284中看到的此示例。

  • 我们必须设置正确的内容类型,并且必须接受正确的内容类型,如添加内容类型:application / json和接受:application / json。否则将采用默认值

0

Dart http的实现通过“ charset = utf-8”来处理字节,因此我确定那里有几种实现都支持此功能,以避免从响应中读取字节时避免使用“ latin-1”后备字符集。就我而言,我完全丢失了响应正文字符串的格式,因此我必须手动对utf8进行字节编码,或者在服务器的API响应中添加该标头“ inner”参数。


0

我正在使用HttpClient并获取内容类型为的响应标头,application/json因为HttpClient默认为ISO-8859-1,所以我丢失了诸如外语或使用unicode的符号之类的字符。因此,如@WesternGun所述,尽可能明确,以避免出现任何可能的问题。

由于服务器无法method.setRequestHeader("accept-charset", "UTF-8");为我处理请求的标题字符集(),因此无法处理,我不得不以绘制字节的形式获取响应数据,并使用UTF-8将其转换为String。因此,建议明确并避免采用默认值。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.