【问题标题】:MultiException when custom jersey param throws exception自定义球衣参数抛出异常时的 MultiException
【发布时间】:2015-07-09 06:04:58
【问题描述】:

注意:所有重现此问题的代码都可以在https://gist.github.com/SrikanthRao/c9fc35e6fe22a74ab40c获得

http://localhost:8080/date/bean?date=2014-13-23(使用 BeanParam)产生 "{"code":500,"message":"处理您的请求时出错。它已被记录 (ID 48be9aa43bd49547)."}" 而没有将 MultiExceptionMapper 添加到球衣。

如果我将 MultiExceptionMapper 添加到球衣,上面的 url 会导致

“日期不是 YYYY-MM-DD 格式或无效”

http://localhost:8080/date?date=2014-13-23(直接@QueryParam 参数)产生 "日期不是 YYYY-MM-DD 格式或无效"

几个问题:

  1. 这是以更简洁的方式处理输入验证的正确方法吗?
  2. 我希望这无需添加我自己的 MultiExceptionMapper 即可工作。 Jersey 不支持在资源方法中作为 @BeanParam 注入的 POJO 中的自定义 *Params 吗?

这是请求时产生的堆栈跟踪(不向球衣添加 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)

我在 dropwizard-user google group 上问过这个问题 - https://groups.google.com/forum/#!topic/dropwizard-user/yW-RXSSlspY

【问题讨论】:

  • 你是否实现了对应的代码: // 这里返回有效的Response对象。
  • 另一点。异常抱怨类 com.fun.personal.finance.params.PaginationFilters。你能检查它是否有一个公共构造函数吗?
  • 是的,我确实实现了返回正确的响应。让我更新帖子,这样更容易理解。还有一件事是我在这里改变了课程,而不是复制我所拥有的一切。堆栈跟踪引用了不同的名称。修复它。没有 args 构造函数。
  • 代码:return Response.serverError();没有为我编译。我不得不附加一个 .build()。此外,@NoArgsConstructor 不是 dropwizard 注释。你在哪里得到它?顺便说一句,当使用 LocalDateTimeParam 作为参数时,错误方法对我来说效果很好。该问题似乎与 PaginationFilters 中的包装有关。您能否提供使用它的代码,以便我可以重现您的问题?
  • 我不明白。你说这个问题解决了吗?而且您在修复之前发布了问题异常,您只想知道 ExceptionMapper 是否是正确的修复?

标签: java jax-rs jersey-2.0 dropwizard


【解决方案1】:

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}

看看要点:https://gist.github.com/jeorfevre/6e46ae8d9232f7f9d7cc

2/ 为了捕获异常,一个好的做法是使用 Providers 捕获服务器级别的异常(通过向您的应用注册提供程序)。 对于应用级异常,请使用 response.status(status of exception) 自行利用您的异常.....

请看一下我的 fork gist,我已经添加了一个 _ServerError.class 并且我已经在应用程序中注册了它:

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

请看清楚的官方文档: official jersey representation doc

【讨论】:

  • 为简单起见,我的示例仅显示了 PaginationFilters 中的一个参数。为许多资源方法提供服务的 REST API 将具有类似的输入(我的意思是日期被多种方法使用等),但其中很多。我想尽可能地将验证分开,而不是每次都重写验证。这个目标驱使我使用 Jersey Params。我认为这就是他们的目的。我知道两个异常映射器。我正在使用现有的约束验证器。当自定义参数位于 @BeanParam pojo 中时,我担心会看到 MultiException。
  • 如果LocalDateTimeParam BeanParam在很多资源中使用,这个解决方案会导致大量重复代码。
【解决方案2】:

问题 1

根据 dropwizard 的核心文档,我看到了两种可能的输入验证实现:

  • 通过验证注解

您可以向表示类的字段添加验证注释并对其进行验证...

这似乎不适合您的情况。事实上,LocalDateTime 没有可用的注解,创建一个会导致解析 LocalDateTime 两次:用于验证和设置 bean 字段。

  • 通过错误处理:

如果你想要更多的控制,你也可以在你的环境中声明 JerseyProviders 来将异常映射到某些响应,方法是调用 JerseyEnvironment#register(Object) 并实现 javax.ws.rs.ext.ExceptionMapper...

为了回答您的第一个问题,我想说在您的情况下使用异常映射器非常好。

问题 2 调试两个 isValidDate 方法表明 @BeanParam 版本使用 ClazzCreator.java 而 @QueryParam 没有。此类负责创建 bean 类实例。如果您检查the class 的第 226 行,您将看到它在解析具有多个错误的输入时收集了多个异常。这应该允许报告与不同 bean 字段相关的多个错误,包括一些后续异常。

答案是 Jeysey 在 POJO 中支持 *Params。但是,相关的异常在@BeanParam 的上下文中被封装成一个MultiException。 因此,如果您打算向 PaginationFilters 添加其他字段,则应考虑在映射的异常消息中报告多个错误。

【讨论】:

  • 我的印象是,约束验证器是在 Jersey 将 QueryParam 从请求转换为 *Param 类之后执行的。为简单起见,我没有在 PaginationFilters 中放置几个​​参数。但这具有将几个参数添加到其中的范围。如果我想合并多个错误,我不知道该怎么做。如果您看到 MultiExceptionMapper,它会找到第一个类型为 WebApplicationException 的错误并返回其响应。此响应有适当的错误。例如,我可以有某种格式的错误有效负载。当我有多个响应时,我不确定如何组合。
  • 这是一个不同的问题。它已在另一个回复中得到解决:stackoverflow.com/questions/28858450/…。您应该能够根据自己的要求对其进行调整。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-14
  • 2011-10-06
  • 2020-08-03
  • 2011-05-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多