2016-11-07 5 views
0

Я пытаюсь реализовать метод в ресурсе dropwizard, который будет обслуживать вызов из интерфейса JS (который использует DataTables).Список десериализации <Map <String, String >> QueryParam in jersey 1

Запрос содержит параметры запроса, которые выглядят следующим образом:

колонки [0] [данные] = 0 & колонки [0] [имя] = & столбцы [0] [поиск] = ложные & столбцы [0] [упорядочиваема] = FALSE & столбцов [0] [поиск] [значение] = & столбцы [0] [поиск] [регулярное выражение] = FALSE

колонны [1] [Data] = ИАТА & столбцы [1 ] [name] = iata & столбцы [1] [поиск] = истинные & столбцы [1] [упорядочиваемый] = истинный & столбцов [1] [Поиск] [значение] = & столбцов [1] [поиск] [регулярное выражение] = FALSE

запрос приходит из интерфейса JS реализованный с DataTables, и использует обработку на сторону сервера. Информация о том, как DataTables посылает запросы здесь:

https://datatables.net/manual/server-side

У меня возникли проблемы, определяющие тип данных для указанных выше параметров запроса. С весенними данными, мы можем определить его как:

List<Map<String, String>> columns 

, которые могут быть обернуты в объект с аннотацией ModelAttribute и будет десериализации хорошо.

В моем приложении я использую более старую версию dropwizard, которая зависит от трикотажа 1.19. Я пробовал аннотировать его как QueryParam, но приложение не работает при запуске.

Метод:

@Path("/mappings") 
@GET 
@Timed 
@Consumes(MediaType.APPLICATION_JSON) 
@Produces(MediaType.APPLICATION_JSON) 
public Response getMappings(@QueryParam("columns") List<Map<String, String>> columns) { 
    // processing here. 
} 

Когда я делаю это, я получаю:

ERROR [2016-11-07 14: 16: 13061] com.sun.jersey.spi.inject. Ошибки: следующие ошибки и предупреждения были обнаружены с помощью ресурсов и/или классов поставщиков: SEVERE: Отсутствует зависимость для метода public javax.ws.rs.core.Response com.ean.gds.proxy.ams.application. resource.gui.IataMappingGuiResource.getMappings (java.util.List) по параметру с индексом 0 WARN [2016-11-07 14: 16: 13070] /: недоступен

Мой вопрос: у меня есть какой-либо другой вариант, кроме написания пользовательского десериализатор для него?

Примечание: если я получаю запрос с помощью @Context, я вижу, что decodedQueryParams являются многозначными картами, которые сопоставляют строковые ключи, такие как «столбцы [0] [данные]», в списки значений String, которые всегда имеют один элемент, то есть значение.

Обновление: После некоторого рытья я нашел следующую спецификацию JAX-RS (раздел 3.2), который объясняет, почему мой подход не является действительным, чтобы начать с:

следующие типы:

  1. Примитивные Типы

  2. типов, которые имеют конструктор, который принимает один Строковый аргумент.

  3. Типы, которые имеют статический метод с именем valueOf с одним аргументом String.

  4. List, Set или SortedSet, где T удовлетворяет 2 или 3 выше.

Источник: Handling Multiple Query Parameters in Jersey

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

+0

Это то, что вам нужно будет разобрать вручную. Или найдите библиотеку, которая знает, как ее разобрать. Джерси недостаточно умен для этого –

+0

Не позволяет ли эта клиентская библиотека отправлять данные в формате JSON? Если вам нужно придерживаться параметров запроса, а не отправлять его в тело, вы все равно можете более легко разбирать JSON, тогда вы сможете сделать этот текущий формат. Я знаю, что большинство JS-библиотек позволяют использовать формат JSON –

+0

@peeskillet К сожалению, это не так. Я не могу контролировать, как библиотека отправляет эти данные в запросе. В настоящее время я собираюсь создать собственный парсер. Благодаря ! –

ответ

1

На самом деле вы используете такую ​​совершенно другую структуру из всех распространенных, которые мы сопоставили для завершения Web-сервисов Rest Web Services. Кроме того, из-за этой проблемы структурного соответствия попытка использования JSON для маршалинга/развязывания значений не устраивает, как только мы не переносим объектно-ориентированные параметры.

Но у нас есть пара вариантов «работать в этой ситуации вокруг». Давайте посмотрим:

  1. Идя со стратегией @QueryParam не представляется возможным из-за двух основных причин:

    • Как вы заметили, есть некоторые ограничения на его использование в отношении Collections кроме Lists, Sets и т.д. ;
    • Эта аннотация отображает один (или список) параметров (ов) по его (их) именам (именам), поэтому вам нужно, чтобы каждый отдельный параметр (разделенный &) имел одно и то же имя. Нам легче подумать о форме, которая представляет (через GET) список значений флажков: после того, как все они имеют одинаковое свойство name, они будут отправлены в формате "name=value1&name=value2".

    Итак, для того, чтобы получить это требование, вы должны сделать что-то вроде:

    @GET 
    public Response getMappings(@QueryParam("columns") List<String> columns) { 
        return Response.status(200).entity(columns).build(); 
    } 
    
    // URL to be called (with same param names): 
    // /mappings?columns=columns[1][name]=0&columns=columns[0][searchable]=false 
    
    // Result: [columns[1][name]=0, columns[0][searchable]=false] 
    

    Вы также можете попробовать создать Пользовательский тип Java для Param аннотаций, как вы see here. Это позволит избежать проблем с кодированием, но в моих тестах это не помогло решить проблему с скобками. :(

  2. Вы можете использовать регулярное выражение вместе с @Path аннотаций определения того, что будет принят параметр String. К сожалению, Ваш URL будет составлен недействительно characteres (как скобки []), что означает, что ваш сервер будет для возврата 500 error.

    Один из вариантов для этого - «заменить» эти символы для действительных (например, символ подчеркивания, например):

    /mappings/columns_1_=0&columns_1__name_= 
    

    Таким образом, решение может быть применено без забот:

    @GET 
    @Path("/{columns: .*}") 
    public Response getMappings(@PathParam("columns") String columns) { 
        return Response.status(200).entity(columns).build(); 
    } 
    
    // Result: columns_1_=0&columns_1__name_= 
    
  3. Гораздо лучший способ сделать это через UriInfo объект, как вы, возможно, пытались. Это проще, потому что нет необходимости изменять URL и параметры. Объект имеет getQueryParameters(), который возвращает Map со значениями из параметров:

    @GET 
    public Response getMappings(@Context UriInfo uriInfo) { 
        MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters(); 
    
        // In case you want to get the whole generated string 
        String query = uriInfo.getRequestUri().getQuery(); 
    
        String output = "QueryParams: " + queryParams 
          + "<br> Keys: " + queryParams.keySet() 
          + "<br> Values: " + queryParams.values() 
          + "<br> Query: " + query; 
    
        return Response.status(200).entity(output).build(); 
    } 
    
    // URL: /mappings?columns[1][name]=0&columns[0][searchable]=false 
    
    /* Result: 
    * QueryParams: {columns[0][searchable]=[false], columns[1][name]=[0]} 
    * Keys: [columns[0][searchable], columns[1][name]] 
    * Values: [[false], [0]] 
    * Query: columns[1][name]=0&columns[0][searchable]=false 
    */ 
    

    Однако, вы должны знать, что если вы будете следовать этому подходу (с помощью Map) вы не можете дублировали ключи, когда структура Безразлично Не поддерживайте его. Вот почему я включаю опцию getQuery(), где вы получаете всю строку.

  4. Последняя возможность создает InjectableProvider, но я не вижу много различий в стратегии getQuery() (так как вы можете разбить ее и создать свою собственную карту значений).
+0

Да, третий вариант - это то, что я использовал. Это дает мне большую гибкость (хотя и не очень) с точки зрения маневрирования данных. Я отмечу этот ответ как принятый. Благодаря ! –

+0

Добро пожаловать! :) – bosco