我在REST API中使用Jersey 2.10和Jackson序列化/反序列化功能。
我的想法是让我的REST API始终返回标准的JSON错误响应。为此,我有ExceptionMapper类,为Jersey应用程序中抛出的任何异常构建正确的json错误响应。我还有一个生成相同类型的JSON响应的jsp,我在web.xml中注册为error-page,它涵盖了在加载Jersey之前可能出现的所有错误。
但有一种情况,我的Exception mappers和我的json生成jsp都没有工作,就是当一个坏的形成的json发送到POST REST端点时,它只返回以下消息:
HTTP/1.1 400 Bad Request
Server: Apache-Coyote/1.1
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, DELETE, PUT
Content-Type: text/plain
Content-Length: 210
Date: Tue, 24 Jun 2014 22:14:11 GMT
Connection: close
Can not deserialize instance of com.example.rest.User[] out of START_OBJECT token
at [Source: org.glassfish.jersey.message.internal.EntityInputStream@1dcccac; line: 1, column: 1]
如何让Jersey返回我的自定义错误响应而非此?
更新
根据@Lucasz的回答,我做了更多的研究,发现在com.fasterxml.jackson.jaxrs.base(https://github.com/FasterXML/jackson-jaxrs-providers/tree/master/base/src/main/java/com/fasterxml/jackson/jaxrs/base)JsonMappingExceptionMapper和JsonParseExceptionMapper包中定义了两个异常映射器,似乎是遮蔽我的自定义映射器。
如何取消注册这些映射器?
这就是我目前正在注册地图制作者的方式:
@ApplicationPath("/")
public class MyApp extends ResourceConfig{
public SyntheticAPIApp() {
packages("com.example.resource", "com.example.mapper");
register(org.glassfish.jersey.jackson.JacksonFeature.class);
}
}
答案 0 :(得分:27)
我使用如下的异常映射器对其进行了测试:
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import com.fasterxml.jackson.core.JsonProcessingException;
@Provider
public class JsonProcessingExceptionMapper implements ExceptionMapper<JsonProcessingException>{
public static class Error {
public String key;
public String message;
}
@Override
public Response toResponse(JsonProcessingException exception) {
Error error = new Error();
error.key = "bad-json";
error.message = exception.getMessage();
return Response.status(Status.BAD_REQUEST).entity(error).build();
}
}
并且有效。
更新:将JsonParseException更改为JsonProcessingException(更一般)
<强> UPDATE2:强> 为了避免注册不需要的映射器替换
register(org.glassfish.jersey.jackson.JacksonFeature.class);
与
register(com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider.class);
查看JacksonFeature的源代码,您就会明白发生了什么。
答案 1 :(得分:2)
我遇到了同样的问题,之前的回答让我得到了解决方案,但并没有让我当前的泽西(2.22)。首先,我需要使用https://jersey.java.net/documentation/latest/representations.html中描述的org.glassfish.jersey.spi.ExtendedExceptionMapper
。
此外,Jersey正在检查异常映射器,该异常映射器尽可能接近抛出的异常(来自org.glassfish.jersey.internal.ExceptionMapperFactory
):
for (final ExceptionMapperType mapperType : exceptionMapperTypes) {
final int d = distance(type, mapperType.exceptionType);
if (d >= 0 && d <= minDistance) {
final ExceptionMapper<T> candidate = mapperType.mapper.getService();
if (isPreferredCandidate(exceptionInstance, candidate, d == minDistance)) {
mapper = candidate;
minDistance = d;
if (d == 0) {
// slight optimization: if the distance is 0, it is already the best case, so we can exit
return mapper;
}
}
}
}
因此,我需要准确映射异常而不是更一般的异常。
最后,我的提供商看起来如下:
@Provider
public final class JsonParseExceptionExceptionHandler implements ExtendedExceptionMapper<JsonParseException> {
@Override
public Response toResponse(final JsonParseException exception) {
exception.printStackTrace();
return Response.status(Response.Status.BAD_REQUEST).entity("JSON nicht in korrektem Format.").build();
}
@Override
public boolean isMappable(final JsonParseException arg0) {
return true;
}
}
答案 2 :(得分:1)
我使用“jackson-jaxrs-json-provider 2.8.8”和JAX-RS 2.0
应用程序类 - 您需要注册ExceptionMapper实现类:
@ApplicationPath("pathApplication")
public class ApplicationConfiguration extends Application{
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<>();
resources.add(YourJAXRSClass.class);
resources.add(JsonJacksonEM.class); //ExceptionMapper class implementation
//others resources that you need...
return resources;
}
}
ExceptionMapper类实现:
@Provider
public class JsonJacksonEM implements ExceptionMapper<JsonParseException>{
@Override
public Response toResponse(JsonParseException exception) {
//you can return a Response in the way that you want!
return Response.ok(new YourObject()).build();
}
}
答案 3 :(得分:0)
我遇到了同样的问题并解决了覆盖ExceptionMapper的问题。完善!我需要做的另一件事是,不理解100%是如何为我的应用程序覆盖JacksonProvider(我不知道它是否与我使用的泽西版本相关 - 2.19)。这是我的web.xml部分覆盖它:
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider
</param-value>
</init-param>
答案 4 :(得分:0)
从Jersey 2.26(1,2)开始,用足够高的Priority
(此处是 high 来注释自定义异常映射器就足够了低且严格为正的数字)。要覆盖org.glassfish.jersey.media:jersey-media-json-jackson
(至register(JacksonFeature.class)
)提供的“默认”映射器,我们仅提供以下两个自定义映射器:
@Provider
@Priority(1)
public class JsonMappingExceptionMapper implements ExceptionMapper<JsonMappingException> {
/* ... */
}
@Provider
@Priority(1)
public class JsonParseExceptionMapper implements ExceptionMapper<JsonParseException> {
/* ... */
}
不幸的是,JAX-RS 2规范忽略了优先级,仅声明:
在选择异常映射提供程序以映射异常时, 实现必须使用其通用类型是异常的最近超类的提供程序。
如另一个答案中所述,未注册JacksonFeature.class
并注册JacksonJaxbJsonProvider.class
并没有带来一致的结果。
答案 5 :(得分:0)
我最近遇到了这个问题,注册 com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider
的解决方案并没有太大帮助。当我深入挖掘时,我开始知道 Jersey 默认情况下 org.glassfish.jersey.jackson.JacksonFeature
是由 Jersey 注册的,如果依赖项 jersey-media-json-jackson
即使没有注册它的显式声明(不确定自动注册是从哪个版本开始的)已实现,我猜至少从 Jersey 2.29 开始)存在于类路径中。
JacksonFeature
会自动注册 JsonParseExceptionMapper
和 JsonMappingExceptionMapper
。由于这些默认的 JSON 异常映射器,所有与 JSON 相关的异常都不会重定向到 custom exception mapper
幸运的是,Jersey 2.29.1 添加了对注册 JacksonFeature 的支持,而无需异常处理程序。链接 feature request link,code changes。
@Provider
public class ApplicationConfig extends Application {
@Override
public Set<Object> getSingletons() {
Set<Object> singletons = super.getSingletons();
singletons.add(JacksonFeature.withoutExceptionMappers());
return singletons;
}
}
以上代码片段将覆盖 Jersey 注册的默认 JacksonFeature
。通过这样做,所有与 JSON 相关的异常都将被重定向到应用程序中存在的自定义异常映射器。