Как работать с accept-параметрами при разработке приложения jax-rs

Чтобы иметь дело с различными версиями типа содержимого, я пытаюсь использовать параметры accept заголовков "Accept*" ( RFC 2616).

Accept: application/vnd.mycompany.mytype;version=2 , application/vnd.mycompany.mytype;version=1;q=0.1

Проблема в том, что аннотации Jax-RS не поддерживают Accept-параметры...

@GET
@Produces("application/vnd.test;version=1")
public Response test1() {
    return Response.ok("Version 1", "application/vnd.test").build();
}

@GET
@Produces("application/vnd.test;version=2")
public Response test2() {
    return Response.ok("Version 2", "application/vnd.test").build();
}

В результате возникает исключение конфликта типов носителей:

Producing media type conflict. The resource methods public javax.ws.rs.core.Response test.resources.TestResource.test2() and public javax.ws.rs.core.Response test.resources.TestResource.test1() can produce the same media type

Может быть, это исключение относится только к моей инфраструктуре JAX-RS (Джерси), но я боюсь, что это связано с JSR311, который не содержит явных параметров accept.

К настоящему времени я использую типы контента, которые содержат версию в своих именах, но я нашел это решение довольно уродливым.

@GET
@Produces("application/vnd.test-v1")
public Response test() {
    return Response.ok("Version 1", "application/vnd.test-v1").build();
}

Есть ли у вас какие-либо идеи о том, как работать с параметрами accept?

РЕДАКТИРОВАТЬ

Я думаю, что я не был достаточно ясен. Я хочу автоматически направить запрос на конкретные методы. Эти методы являются версионными и соответствуют определенной версии возвращаемого типа содержимого. Текущая реализация JAX-RS не позволяет мне использовать accept-параметры для направления запроса (к соответствующему методу).

Гринкод предлагает мне управлять version параметр accept в методе диспетчеризации (используя @HeaderParam("Accept")). Это решение в конечном итоге переписывает логику отрицательного обращения к контенту, которая встроена в структуру (и описана в JSR 311).

Что я могу сделать, чтобы использовать как логику принятия параметров, так и логику отрицательного отношения к контенту из JAX-RS?

Возможно, решение состоит в том, чтобы использовать другой фреймворк (сейчас я работал только с Джерси). Но я не знаю какой.

3 ответа

Решение

В спецификации JAX-RS явно не говорится ничего о игнорировании параметров заголовка Accept. Но единственным параметром, для которого определенно определена обработка, является качество (q). Это возможная область для улучшения, так как она, кажется, привела к двусмысленности (или откровенной ошибочности) в реализации на Джерси. Текущая версия Jersey (1.17) не учитывает параметры заголовка Accept при сопоставлении входящих запросов с методами ресурсов, поэтому вы получаете сообщение об ошибке:

ТЯЖЕЛАЯ: Создание конфликта медиа-типа. Методы ресурса...

Для ресурса:

@GET
@Produces("application/vnd.test;version=1")
public Response test1() {
    return Response.ok("Version 1", "application/vnd.test").build();
}

@GET
@Produces("application/vnd.test;version=2")
public Response test2() {
    return Response.ok("Version 2", "application/vnd.test").build();
}

Похоже, что Джерси выполняет проверку "уникальности" на основе заголовка Accept "тип / подтип", полностью пропуская любые параметры. Это может быть подтверждено тестированием с различными парами заголовков в "соответствующих" методах ресурса:

Ресурс 1 Ресурс 2
----------------------------------------
текст /html;q=0,4 текст /html;q=0,8
text/html             text/html;q=0,2
text/html             text/html;qs=1.4
text/html;qs=1.4      text/html;qs=1.8
текст / html; уровень = 1 текст / html; уровень =2
text/html;foo=bleh    text/html;bar=23

Все терпят неудачу с той же ошибкой. Если было сделано предположение, что когда-либо отправляется только параметр качества, то имеет смысл сопоставлять только "тип / подтип", потому что этот тип запроса бессмысленный:

Принять: текст /html;q=0,8, текст /html;q=0,4, текст / html

Ака, параметры качества имеют смысл только тогда, когда вы имеете дело со смесью возможных типов контента. Однако этот вид ограниченного соответствия завершается неудачно, когда отправляются некачественные параметры или дополнительные параметры:

Принять: текст / html; версия =4.0;q=0.8, текст / html; версия =3.2;q=0.4

Итак, каковы возможные решения?

  • Перехватите высокоуровневый запрос, основанный на "типе / подтипе", затем перейдите к более подходящему методу (вы указали, что не хотите этого делать)
  • Измените ваши ожидаемые заголовки. Например, "application/vnd.mycompany.mytype+v2" и "application/vnd.mycompany.mytype+v1". Никаких других изменений не потребуется, и вы можете продолжать использовать Джерси
  • Переключить рамки. RESTEasy легко справляется с вашим сценарием.

С RESTEasy и ресурсом:

@Path("/content/version")
public class ContentVersionResource {

    @GET
    @Produces("application/vnd.test;version=1")
    public Response test1() {
        return Response.ok("Version 1", "application/vnd.test").build();
    }

    @GET
    @Produces("application/vnd.test;version=2")
    public Response test2() {
        return Response.ok("Version 2", "application/vnd.test").build();
    }
}

Успешное совпадение достигается с помощью следующего заголовка Accept:

Принять: application/vnd.test; версия =1;q=0,3; application / vnd.test; версия = 2; q = 0,5
Ответ: версия 2

И это:

Принять: application/vnd.test; версия =1;q=0,5; application / vnd.test; версия = 2; q = 0,3
Ответ: Версия 1

Вы можете скачать и протестировать этот пример проекта. Требуются Git, Maven и JBoss 7.x

Если я что-то упустил. JAX-RS поддерживает параметры Accept. посмотри на @Consumes("*/*") аннотаций. Кроме того, исключение, которое вы получаете с конфликтом типов носителей, происходит потому, что у вас есть два GET методы по тому же адресу. аннотируйте метод test2() с помощью @Path("test2"), а затем отправьте GET вместо этого запросите URL /test2. это должно избавиться от этой ошибки.

РЕДАКТИРОВАТЬ

Вы можете ввести значение Accept использование заголовка @HeaderParams, Вот пример того, что я сделал.

@Path("/conneg")
public class ConnNeg {

    @GET
    @Produces("application/vnd.test;version=1")
    public Response test1(@HeaderParam("Accept") String header) {
        System.out.println(header);
        return Response.ok("Version 1", "application/vnd.test").build();
    }
}

прохождение запроса

Принять: application/vnd.test; версия =2, application/vnd.test; версия =1;q=0,1

это напечатает

application / vnd.test; версия =2, application/vnd.test; версия =1;q=0,1

Затем вы можете справиться с этим вручную. Это то, что вы ищете?

В структуре Джерси заголовок Accept HTTP-запроса объявляет, что является наиболее приемлемым. Если класс ресурсов способен создавать более одного типа мультимедиа MIME, то выбранный метод ресурса будет соответствовать наиболее приемлемому типу мультимедиа, как объявлено клиентом. В вашем случае, если заголовок подтверждения

Accept: application/vnd.mycompany.mytype;version=2

тогда будет вызван метод test1().

Если это

Accept: application/vnd.mycompany.mytype;q=0.9 version=2, application/vnd.mycompany.mytype;version=1

последний будет называться.

В одном объявлении @Produces может быть объявлено несколько типов носителей, например:

@GET
@Produces({"application/vnd.mycompany.mytype; version=2", "application/vnd.mycompany.mytype; version=1"})
public Response test() {
    return Response.ok("").build();
}

метод test(9) будет вызван, если приемлем один из двух медиатипов. Если оба приемлемы, будет вызван первый.

Надеюсь, поможет!

Другие вопросы по тегам