自定义球衣参数抛出异常时的MultiException
MultiException when custom jersey param throws exception
注意:可在 https://gist.github.com/SrikanthRao/c9fc35e6fe22a74ab40c
获取重现此问题的所有代码
http://localhost:8080/date/bean?date=2014-13-23(使用 BeanParam)产生
"{"code":500,"message":"There was an error processing your request. It has been logged (ID 48be9aa43bd49547)."}" 没有将 MultiExceptionMapper 添加到球衣。
如果我将 MultiExceptionMapper 添加到 jersey,上面的 url 结果是
"Date is either not in YYYY-MM-DD format or is invalid"
http://localhost:8080/date?date=2014-13-23(直接@QueryParam参数)产生
"Date is either not in YYYY-MM-DD format or is invalid"
几个问题:
- 这是以更简洁的方式处理输入验证的正确方法吗?
- 我希望这无需添加我自己的 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 组中问过这个问题 - https://groups.google.com/forum/#!topic/dropwizard-user/yW-RXSSlspY
问题 1
根据 dropwizard 的核心文档,我看到了两种可能的输入验证实现方式:
- 通过验证注解
you can add validation annotations to fields of your representation classes and validate them...
这似乎不适合你的情况。事实上,LocalDateTime 没有可用的注释,创建一个注释会导致解析 LocalDateTime 两次:用于验证和设置 bean 字段。
- 通过错误处理:
If you want more control, you can also declare JerseyProviders in your Environment to map Exceptions to certain responses by calling JerseyEnvironment#register(Object) with an implementation of javax.ws.rs.ext.ExceptionMapper...
为了回答您的第一个问题,我会说在您的情况下使用异常映射器完全没问题。
问题 2
调试两个 isValidDate 方法表明 @BeanParam 版本使用 ClazzCreator.java 而 @QueryParam 不使用。此 class 负责创建 bean class 实例。如果您检查 the class 的第 226 行,您会看到它在解析具有多个错误的输入时收集了多个异常。这应该允许报告与不同 bean 字段相关的几个错误,包括一些后续异常。
答案是 Jeysey 支持 POJO 中的 *Params。但是,相关异常封装在@BeanParam 上下文中的 MultiException 中。
因此,如果您计划向 PaginationFilters 添加其他字段,则应考虑在映射的异常消息中报告多个错误。
1/ 为了验证日期,我更喜欢自己解析 "dates"。它简单干净。这是一些工作代码:
package com.rizze.Whosebug;
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
将 return :
{"time":1389496320000}
看一下要点:https://gist.github.com/jeorfevre/6e46ae8d9232f7f9d7cc
2/ 为了捕获异常,一个好的做法是使用提供程序捕获服务器级别的异常(通过向您的应用程序注册提供程序)。
对于应用程序级别的异常,请使用 response.status(异常状态) .....
自行利用您的异常
请看一下我的 fork gist,我添加了一个 _ServerError.class 并在应用程序中注册了它:
//register a mapper by rizze
environment.jersey().register(_ServerError.class);
请看官方文档,非常清楚:
official jersey representation doc
注意:可在 https://gist.github.com/SrikanthRao/c9fc35e6fe22a74ab40c
获取重现此问题的所有代码http://localhost:8080/date/bean?date=2014-13-23(使用 BeanParam)产生 "{"code":500,"message":"There was an error processing your request. It has been logged (ID 48be9aa43bd49547)."}" 没有将 MultiExceptionMapper 添加到球衣。
如果我将 MultiExceptionMapper 添加到 jersey,上面的 url 结果是
"Date is either not in YYYY-MM-DD format or is invalid"
http://localhost:8080/date?date=2014-13-23(直接@QueryParam参数)产生 "Date is either not in YYYY-MM-DD format or is invalid"
几个问题:
- 这是以更简洁的方式处理输入验证的正确方法吗?
- 我希望这无需添加我自己的 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 组中问过这个问题 - https://groups.google.com/forum/#!topic/dropwizard-user/yW-RXSSlspY
问题 1
根据 dropwizard 的核心文档,我看到了两种可能的输入验证实现方式:
- 通过验证注解
you can add validation annotations to fields of your representation classes and validate them...
这似乎不适合你的情况。事实上,LocalDateTime 没有可用的注释,创建一个注释会导致解析 LocalDateTime 两次:用于验证和设置 bean 字段。
- 通过错误处理:
If you want more control, you can also declare JerseyProviders in your Environment to map Exceptions to certain responses by calling JerseyEnvironment#register(Object) with an implementation of javax.ws.rs.ext.ExceptionMapper...
为了回答您的第一个问题,我会说在您的情况下使用异常映射器完全没问题。
问题 2 调试两个 isValidDate 方法表明 @BeanParam 版本使用 ClazzCreator.java 而 @QueryParam 不使用。此 class 负责创建 bean class 实例。如果您检查 the class 的第 226 行,您会看到它在解析具有多个错误的输入时收集了多个异常。这应该允许报告与不同 bean 字段相关的几个错误,包括一些后续异常。
答案是 Jeysey 支持 POJO 中的 *Params。但是,相关异常封装在@BeanParam 上下文中的 MultiException 中。 因此,如果您计划向 PaginationFilters 添加其他字段,则应考虑在映射的异常消息中报告多个错误。
1/ 为了验证日期,我更喜欢自己解析 "dates"。它简单干净。这是一些工作代码:
package com.rizze.Whosebug;
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
将 return :
{"time":1389496320000}
看一下要点:https://gist.github.com/jeorfevre/6e46ae8d9232f7f9d7cc
2/ 为了捕获异常,一个好的做法是使用提供程序捕获服务器级别的异常(通过向您的应用程序注册提供程序)。 对于应用程序级别的异常,请使用 response.status(异常状态) .....
自行利用您的异常请看一下我的 fork gist,我添加了一个 _ServerError.class 并在应用程序中注册了它:
//register a mapper by rizze
environment.jersey().register(_ServerError.class);
请看官方文档,非常清楚: official jersey representation doc