MultiException, когда исключение пользовательского jersey param исключает

ПРИМЕЧАНИЕ. Весь код для воспроизведения этой проблемы можно найти в https://gist.github.com/SrikanthRao/c9fc35e6fe22a74ab40c

http://localhost:8080/date/bean?date=2014-13-23 (используя BeanParam) производит   "{" code ": 500," message ":" Произошла ошибка обработки вашего запроса. Он был зарегистрирован (ID 48be9aa43bd49547). "}" Без добавления MultiExceptionMapper в трикотаж.

Если я добавлю MultiExceptionMapper в jersey, указанный выше URL-адрес приведет к

"Дата либо не в формате ГГГГ-ММ-ДД, либо недействительна"

http://localhost:8080/date?date=2014-13-23 (прямой параметр @QueryParam) производит "Дата не указана в формате ГГГГ-ММ-ДД или недействительна"

Пара вопросов:

  • Является ли это правильным способом обработки ввода с помощью более чистого способа?
  • Я ожидал, что это сработает без необходимости добавления моего собственного MultiExceptionMapper. Не поддерживает ли Джерси пользовательские * Парамы внутри POJO, которые вводятся как @BeanParam в методах ресурсов?

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

    ERROR [2015-05-04 18:48:33,366] io.dropwizard.jersey.errors.LoggingExceptionMapper: Error handling a request: 0f23e4de758653d6
    ! javax.ws.rs.WebApplicationException: HTTP 400 Bad Request
    ! at io.dropwizard.jersey.params.AbstractParam.<init>(AbstractParam.java:28) ~[dropwizard-jersey-0.8.1.jar:0.8.1]
    ! at com.fun.myapp.LocalDateTimeParam.<init>(LocalDateTimeParam.java:20) ~[classes/:na]
    ! at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_45]
...
...
Causing: org.glassfish.hk2.api.MultiException: A MultiException has 3 exceptions.  They are:
! 1. javax.ws.rs.WebApplicationException: HTTP 400 Bad Request
! 2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.fun.myapp.PaginationFilters errors were found
! 3. java.lang.IllegalStateException: Unable to perform operation: resolve on com.fun.myapp.PaginationFilters
! 
! at org.jvnet.hk2.internal.Collector.throwIfErrors(Collector.java:88) ~[hk2-locator-2.4.0-b10.jar:na]
! at org.jvnet.hk2.internal.ClazzCreator.resolveAllDependencies(ClazzCreator.java:252) ~[hk2-locator-2.4.0-b10.jar:na]
! at org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:360) ~[hk2-locator-2.4.0-b10.jar:na]
! at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:471) ~[hk2-locator-2.4.0-b10.jar:na]
....
....
WARN  [2015-05-04 18:48:33,401] org.glassfish.jersey.internal.Errors: The following warnings have been detected: WARNING: Unknown HK2 failure detected:
MultiException stack 1 of 3
javax.ws.rs.WebApplicationException: HTTP 400 Bad Request
    at io.dropwizard.jersey.params.AbstractParam.<init>(AbstractParam.java:28)
    at com.fun.myapp.LocalDateTimeParam.<init>(LocalDateTimeParam.java:20)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
...
...
MultiException stack 2 of 3
java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.fun.myapp.PaginationFilters errors were found
    at org.jvnet.hk2.internal.ClazzCreator.resolveAllDependencies(ClazzCreator.java:249)
    at org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:360)
    at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:471)
...
...
MultiException stack 3 of 3
java.lang.IllegalStateException: Unable to perform operation: resolve on com.fun.myapp.PaginationFilters
    at org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:389)
    at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:471)
    at org.glassfish.jersey.process.internal.RequestScope.findOrCreate(RequestScope.java:162)

Я задал этот вопрос в группе google-группы dropwizard - https://groups.google.com/forum/#!topic/dropwizard-user/yW-RXSSlspY

Ответ 1

Вопрос 1

В соответствии с документацией ядра dropwizard я вижу две возможные реализации для проверки ввода:

  • через аннотацию проверки

вы можете добавить аннотации проверки к полям ваших классов представления и проверить их...

Это не подходит для вашего дела. Фактически, нет никакой аннотации для LocalDateTime, и создание одного приводит к анализу LocalDateTime дважды: для проверки и для установки поля bean.

  • Обработка ошибок:

Если вы хотите большего контроля, вы также можете объявить JerseyProviders в своей среде для сопоставления Исключения с определенными ответами, вызвав файл JerseyEnvironment # register (Object) с реализацией javax.ws.rs.ext.ExceptionMapper...

Чтобы ответить на ваш первый вопрос, я бы сказал, что использование оптимизатора исключений отлично подходит для вашего случая.

Вопрос 2 Отладка методов isValidDate показывает, что версия @BeanParam использует ClazzCreator.java, а @QueryParam этого не делает. Этот класс отвечает за создание экземпляра класса bean. Если вы проверите строку 226 класс, вы увидите, что она собирает несколько исключений при анализе ввода с несколькими ошибками. Это должно позволить сообщать о нескольких ошибках, связанных с разными полями bean, включая некоторые последующие исключения.

Ответ заключается в том, что Jeysey поддерживает * Params внутри POJO. Однако связанное исключение инкапсулируется в MultiException в контексте @BeanParam. Следовательно, если вы планируете добавлять другие поля в PaginationFilters, вам следует рассмотреть возможность представления нескольких ошибок в отображаемом сообщении об исключении.

Ответ 2

1/, чтобы подтвердить дату, я предпочитаю самостоятельно разбирать "даты". Это просто и чисто. Вот какой рабочий код:

package com.rizze.stackoverflow;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;



@Path("/test")
public class TestRessource {


    @GET    
    @Path("is")
    @Produces({"application/json"})
    public Response isDateValid(@QueryParam("date") String dateString){

        if(dateString == null) 
            return Response.status(400).entity("date is null").build();
        Date thedate =null;
        try {
            thedate = new SimpleDateFormat("yyyy-mm-dd").parse(dateString);
        } 
        catch (ParseException e) {
            return Response.status(400).entity("date malformed").build();
        }
        System.out.println("time:"+thedate.getTime());
        return Response.ok()
                .entity("{\"time\":"+ thedate.getTime() +"}")
                .header("source", "com.rizze")
                .build();
    }
}

в этом коде вы вызываете

http://localhost:8080/test/is?date=2014-12-12

вернется:

{"time":1389496320000}

взгляните на gist: https://gist.github.com/jeorfevre/6e46ae8d9232f7f9d7cc

2/, чтобы поймать исключения, рекомендуется перехватить исключение на уровне сервера с помощью Провайдеров (зарегистрировав провайдера в своем приложении). И для исключения на уровне приложения, используйте свое исключение самостоятельно, используя response.status(статус исключения).....

Пожалуйста, взгляните на мою вилку, я добавил _ServerError.class, и я зарегистрировал его в приложении:

  //register a mapper by rizze
        environment.jersey().register(_ServerError.class);

Пожалуйста, взгляните на официальный документ, который является четким: официальный представитель dersey