Что означает «Контент-тип: application / json; charset = utf-8 ”действительно значит?

284

Когда я делаю POST-запрос с телом JSON в свой REST-сервис, я включаю его Content-type: application/json; charset=utf-8в заголовок сообщения. Без этого заголовка я получаю ошибку от сервиса. Я также могу успешно использовать Content-type: application/jsonбез ;charset=utf-8порции.

Что именно делает charset=utf-8? Я знаю, что это определяет кодировку символов, но сервис работает без него. Эта кодировка ограничивает символы, которые могут быть в теле сообщения?

DenaliHardtail
источник
4
взгляните на hanselman.com/blog/…
Даниэль Пауэлл
8
Интересно, что в соответствии с регистрацией типов носителей IANA,application/json похоже, что поддерживаемый charsetпараметр вообще отсутствует, хотя часто применяется на практике.
Uux
1
I know it specifies the character encoding but the service works fine without it.«работа» не всегда означает, что «существующий код / ​​конфигурация является наиболее правильным способом, охватывающим все ключевые случаи, чтобы сделать одну вещь». Это зависит от всех условностей и предположений, которые могут не работать при других обстоятельствах. Лично я всегда стараюсь быть максимально откровенным.
WesternGun
3
Отправка параметра "charset" неверна и бессмысленна. См. RFC 8259, раздел 11, последнее предложение.
Джулиан Решке

Ответы:

283

Заголовок просто указывает, во что закодирован контент. Не обязательно можно определить тип контента из самого контента, т.е. вы не можете просто смотреть на контент и знать, что с ним делать. Для этого и нужны 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, и на самом деле делаете это, то да, вы ограничены 256 символами, которые вы можете кодировать в Latin1.

децезе
источник
4
Конечно, в JSON вы все еще можете представлять символы не из Latin1, используя escape-последовательности, такие как \u20AC.
Ден04
31
Согласно стандарту для json вам фактически не разрешено использовать latin1 для кодирования содержимого. Содержимое JSON должно быть закодировано как Unicode, будь то UTF-8, UTF-16 или UTF-32 (с прямым или младшим порядковым номером).
Даниэль Луна
20
В приложении / json нет параметра charset.
Джулиан Решке
7
@DanielLuna прав, application/jsonдолжен быть в одном из форматов преобразования ucs. Кроме того, поскольку первые четыре байта JSON ограничены, вы всегда можете определить, является ли он 8, 16 или 32 и его порядковый номер.
Джейсон Коко
4
Событие, если оно избыточно, вы можете включить charset=utf-8по соображениям безопасности: github.com/shieldfy/API-Security-Checklist/issues/25
manuc66
143

Чтобы обосновать утверждение @ 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
Дрю Ноакс
источник
12
Всегда полезно думать о JSON как о двоичном формате, а не о текстовом формате.
Sulthan
2
Теперь, когда RFC4627 устарел в RFC7159, в котором говорится, что корневое значение может быть строкой (в отличие от предыдущей спецификации), как это теперь реализовано? Спецификация в этом отношении расплывчата и просто говорит о том, что допустимо три кодирования, но не то, как предполагается их дифференцировать.
Фабио Бельтрамини
4
@FabioBeltramini Вышеприведенное все еще должно сохраняться, потому что строка в JSON не будет содержать никаких буквенных нулевых символов (нули в JSON должны быть закодированы с помощью числовой escape-последовательности т.е. "\u0000").
Томасруттер
3
На самом деле второй символ в UTF-16xx может не иметь значения NULL в этом случае, но все равно будет возможно определить кодирование из других байтов: xx 00 00 00все еще UTF-32LE и xx 00 xx xxвсе еще UTF-16LE, 00 xx xx xxвсе еще UTF-16BE.
Томасруттер
20

Обратите внимание, что IETF RFC4627 был заменен IETF RFC7158 . В разделе [8.1] он убирает текст, процитированный ранее @Drew, говоря:

Implementations MUST NOT add a byte order mark to the beginning of a JSON text.
Alex
источник
Однако это предположение остается верным, поскольку любой действительный json по-прежнему будет начинаться с двух символов ascii.
Ларсинг
Один из символов, так как одно числительное является допустимым файлом в формате JSON
Nayuki
0

Я полностью согласен с @deceze, но я хочу развить эту часть вопроса «Я получаю ошибку от службы» ,

Мы получаем такие ошибки как http 415

Http 415 Неподдерживаемая ошибка типа носителя

Код ответа об ошибке клиента HTTP 415 Unsupported Media Type указывает, что сервер отказывается принять запрос, потому что формат полезной нагрузки находится в неподдерживаемом формате.

Проблема с форматированием может быть связана с указанным в запросе Content-Type или Content-Encoding или из-за прямой проверки данных.

Другими словами, как показано в https://stackoverflow.com/a/22643964/914284 в этом примере.

  • Мы должны установить правильный тип контента, и мы должны принять правильный тип контента, как показано. Добавить Content-Type: application / json и Accept: application / json. В противном случае он примет значение по умолчанию
Хамит ЙИЛДИРИМ
источник
0

Реализация Dart http обрабатывает байты благодаря этому charset = utf-8, так что я уверен, что несколько реализаций поддерживают это, чтобы избежать «латинской-1» кодировки отката при чтении байтов из ответа. В моем случае я полностью теряю формат в строке тела ответа, поэтому мне приходится вручную кодировать байты в utf8 или добавлять этот параметр "inner" заголовка в ответ API моего сервера.

roipeker
источник
0

Я использовал HttpClient и получал ответный заголовок с типом содержимого application/json, я потерял символы, такие как иностранные языки или символ, который использовал Unicode, так как HttpClient по умолчанию является ISO-8859-1 . Так что будьте явными, как упомянуто @WesternGun, чтобы избежать любой возможной проблемы.

Нет способа справиться с тем, что из-за того, что сервер не обрабатывает charset ( method.setRequestHeader("accept-charset", "UTF-8");) запрашиваемого заголовка для меня, мне пришлось извлекать данные ответа в виде байтов отрисовки и преобразовывать их в String, используя UTF-8. Поэтому рекомендуется быть явным и избегать предположения о значении по умолчанию.

Три Нгуен
источник