簡體   English   中英

Jackson JsonParseExceptionMapper和JsonMappingExceptionMapper陰影自定義映射器

[英]Jackson JsonParseExceptionMapper and JsonMappingExceptionMapper shadows custom mapper

我的項目使用Spring Boot + Jersey 2。

我為JsonParseException創建了自定義的Jackson映射器,但沒有被調用,而是使用了標准的Jackson JsonParseExceptionMapper。

我的自定義映射器:

 package com.rmn.gfc.common.providers;
 import ...

 @Provider
 public class JsonParseExceptionHandler implements ExceptionMapper<JsonParseException> { 
    @Override
    public Response toResponse(JsonParseException exception) {
        return Response
                .status(Response.Status.BAD_REQUEST)
                //  entity ...
                .build();
    }
}

我這樣注冊我的映射器:

@Component
public class OrderServiceResourceConfig extends ResourceConfig {
    public OrderServiceResourceConfig() {
        packages("com.rmn.gfc.common.providers");
    }
}

我確定映射器注冊是可以的,因為此程序包中的其他自定義映射器正在工作,但是Jersey標准的JsonParseExceptionMapper遮蓋了JsonParseException的映射器。

如何在實現中覆蓋標准的Jackson Jackson JsonParseExceptionMapper?

您可以責怪JacksonFeature

JacksonFeature注冊了傑克遜的例外默認異常映射器如果JacksonJaxbJsonProvider未注冊。 而這正是您所不想要的。

參見源代碼的相關部分:

 // Register Jackson. if (!config.isRegistered(JacksonJaxbJsonProvider.class)) { // add the default Jackson exception mappers context.register(JsonParseExceptionMapper.class); context.register(JsonMappingExceptionMapper.class); ... } 

Spring Boot注冊了JacksonFeature

如果Spring Boot的JerseyAutoConfiguration類位於類路徑中,則它將注冊JacksonFeature 參見源代碼的相關部分:

 @ConditionalOnClass(JacksonFeature.class) @ConditionalOnSingleCandidate(ObjectMapper.class) @Configuration static class JacksonResourceConfigCustomizer { ... @Bean public ResourceConfigCustomizer resourceConfigCustomizer( final ObjectMapper objectMapper) { addJaxbAnnotationIntrospectorIfPresent(objectMapper); return (ResourceConfig config) -> { config.register(JacksonFeature.class); config.register(new ObjectMapperContextResolver(objectMapper), ContextResolver.class); }; } ... } 

解決方法

解決方法是,您可以注冊JacksonJaxbJsonProvider ,然后注冊自定義異常映射器(或僅使用@Provider進行注釋,以使Jersey自動發現它們):

@Component
public class OrderServiceResourceConfig extends ResourceConfig {

    public OrderServiceResourceConfig() {
        packages("com.rmn.gfc.common.providers");
        register(JacksonJaxbJsonProvider.class);
        // Register other providers
    }
}

查看文檔中有關JacksonJaxbJsonProvider

JSON內容類型提供程序已自動配置為同時使用Jackson和JAXB批注(按優先級順序)。 其他方面與JacksonJsonProvider相同。

替代解決方案

或者,您可以擺脫jersey-media-json-jackson工件,而使用jackson-jaxrs-json-provider 這樣,您將擺脫JacksonFeature ,然后可以注冊自己的異常映射器。

有人在這個答案中提到過。

似乎是正確的解決方案

請參閱以下來自JAX-RS 2.1規范的報價:

4.1.3優先級

應用程序提供的提供程序使開發人員能夠擴展和定制JAX-RS運行時。 因此,如果需要單個提供程序,則始終應首選由應用程序提供的提供程序,而不是預先打包的提供程序。

應用程序提供的提供程序可以使用@Priority進行注釋。 如果兩個或更多提供者是某個任務的候選者,則選擇優先級最高的提供者:在這種情況下,最高優先級被定義為值最低的提供者 也就是說, @Priority(1)高於@Priority(10) 如果兩個或多個提供者有資格並且具有相同的優先級,則以與實現相關的方式選擇一個。

所有應用程序提供的提供程序的默認優先級是javax.ws.rs.Priorities.USER 關於優先級的一般規則對於過濾器和攔截器是不同的,因為這些提供程序被收集到鏈中。

就像在Kysil Ivan的答案中指出的那樣,編寫您自己的異常映射器,並將其設置為高優先級,例如1 如果使用自動發現,只需使用@Provider@Priority對其進行注釋。

@Provider
@Priority(1)
public class JsonParseExceptionMapper implements ExceptionMapper<JsonParseException> {
    ...
}

如果您手動注冊提供商,則可以為您的提供商設置綁定優先級

@ApplicationPath("/")
public class MyResourceConfig extends ResourceConfig {

    public MyResourceConfig() {
        register(JsonParseExceptionMapper.class, 1);
    }
}

我找到了適合我的解決方案。
在自定義映射器上使用javax.annotation.Priority使其覆蓋Jackson的默認映射器,例如:

@Provider
@Priority(1)
public class JsonParseExceptionHandler implements ExceptionMapper<JsonParseException> {
    // ...
}

或,如果通過ResourceConfig注冊JAX-RS組件,則可以這樣指定優先級:

public class MyResourceConfig extends ResourceConfig {
    public MyResourceConfig() {
        register(JsonMappingExceptionHandler.class, 1);
        register(JsonParseExceptionHandler.class, 1);
        // ...
    }
}

數字越小優先級越高。
javax.ws.rs.Priorities具有一些用於優先級的預定義常量。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM