Является ли хорошей практикой использование @Produces ("application / json") на всех конечных точках создания JSON? - PullRequest
0 голосов
/ 04 июля 2018

Мы начали использовать Jersey / JAX-RS для внутренних конечных точек REST, которые используются нашим внешним кодом. Конечные точки, которые должны возвращать результат, всегда отправляют объекты JSON.

В целях отладки мы используем расширение firefox restclient . До недавнего времени я просто вводил URL-адрес и нажимал «Отправить» и возвращал содержимое в формате JSON.

Но когда я сделал это этим утром, расширение FF возвращается и говорит мне, что мне нужно изменить тип ответа на двоичный (BLOB). Это приводит к отображению закодированной строки вместо JSON.

Я могу решить эту проблему, установив заголовок запроса (Accept: будет application/json).

Проведя еще какое-то исследование, я наткнулся на этот вопрос . Мой вывод таков: вероятно, мы должны добавить @Produces("application/json") ко всем этим конечным точкам.

Вопрос: действительно ли это так просто, или есть веские технические причины, чтобы не сделать это?

Ответы [ 2 ]

0 голосов
/ 05 июля 2018

Вы должны всегда объявлять аннотации @Produces и @Consumes (либо на уровне класса, либо на уровне метода) с целью согласование содержимого и правильность протокола HTTP. Без этих аннотаций результат будет зависеть от запроса клиента и поведения сервера по умолчанию (которое может отличаться в разных реализациях), что приводит к непредсказуемым и неоднозначным результатам.

С этими аннотациями мы рекламируем , какие типы носителей мы можем производить и потреблять. При запросах Retrieve (GET) клиент должен отправить заголовок Accept с типом носителя того ресурса, который он ожидает. А при создании запросов (PUT, POST) клиент должен отправить заголовок Content-Type, сообщающий серверу, какой тип носителя представляют данные, которые они отправляют. Если эти заголовки не соответствуют тому, что сервер объявляет для обработки, тогда клиент получит ответ об ошибке, сообщающий им, в чем проблема; с запросом Retrieve и несовпадающим заголовком Accept ответом будет 406 Not Acceptable . С запросом на создание и несоответствующим заголовком Content-Type ответом будет 415 Неподдерживаемый тип носителя .

Так работает согласование контента. И чтобы убедиться, что наш сервер ведет себя так, как ожидают клиенты, мы должны объявить, что мы можем обработать на сервере. Аннотации делают именно это.

Как вы упомянули, когда вы остановили @Produces, клиент сказал вам, что вам нужно изменить тип ответа. Это связано с тем, что в результате заголовок ответа Content-Type был установлен на application/octet-stream, что из ответов . Клиенты используют заголовок Content-Type, чтобы определить, как обрабатывать ответ.

Последний пример был для запроса Retrieve. Если мы остановим @Consumes на конечной точке Create, много разных вещей может пойти не так. Например, у нас есть конечная точка, которую мы хотим принять JSON, поэтому мы создаем POJO для сопоставления JSON.

@POST
public Response create(Customer customer) {}

Чтобы это работало, это зависит от того, установил ли клиент заголовок Content-Type в запросе на application/json. Но без аннотации @Consumes мы в основном рекламируем эту конечную точку, чтобы иметь возможность принимать любой тип медиа, что просто смешно. Аннотация @Consumes действует как охранник, говорящий: «Если вы не отправите правильный тип данных, вы не сможете пройти». Но так как у нас нет защиты, все данные пропускаются, и результат непредсказуем, потому что в зависимости от того, что клиент устанавливает Content-Type, мы не знаем, что MessageBodyReader 1 будет обрабатывать преобразование из тела сущности в Customer. Если правильный MessageBodyReader не выбран (тот, который преобразует JSON в POPJO), то, скорее всего, это приведет к исключению, и клиент получит обратно 500 Internal Server Error, которая не так специфична, как получение 415 Неподдерживаемый тип носителя.


1. См. Главы 8 и 9 документации Джерси . Он объяснит, как тела сущностей преобразуются в объекты Java (и наоборот), используя MessageBodyReader и MessageBodyWriter соответственно.

0 голосов
/ 04 июля 2018

Полезно ли использовать @Produces("application/json") на всех конечных точках, производящих JSON?

Если ваши методы ресурсов генерируют JSON в качестве представления ваших ресурсов, они должны быть аннотированы @Produces(MediaType.APPLICATION_JSON). В результате ответ будет иметь заголовок Content-Type, указывающий тип мультимедиа полезной нагрузки.

Аннотация @Produces также используется для сопоставления запроса : среда выполнения JAX-RS соответствует типу носителя, отправляемому в заголовке Accept, с тип носителя, определенный в аннотации @Produces.


Если вы не хотите аннотировать каждый метод ресурса в вашем приложении, вы можете аннотировать классы ресурса вместо этого. Это будет означать, что все методы, определенные в таком классе, должны генерировать JSON как представление ваших ресурсов.


Тип носителя, определенный в аннотации @Produces, указывает тип носителя, который будет создан экземплярами MessageBodyWriter, зарегистрированными в приложении. Рассмотрим следующий пример:

@GET
@Produces(MediaType.APPLICATION_JSON)
public Foo getFoo() {
    Foo foo = new Foo();
    return Response.ok(foo).build();
}

Как только метод getFoo() аннотируется с помощью @Produces(MediaType.APPLICATION_JSON), JAX-RS запишет экземпляр Foo как документ JSON. Это сделано в реализации MessageBodyWriter. Например, если ваше приложение использует Jackson , JacksonJsonProvider будет использоваться для преобразования объектов Java в документы JSON.

...