簡體   English   中英

當序列化有條件地跳過符合 Jackson 自定義標准的對象時修改 HTTP 代碼(Spring 啟動)

[英]Modify HTTP code when Serialize skipping objects conditionally that meet a Custom Criteria with Jackson (Spring boot)

因此,我能夠按照提供的鏈接對滿足自定義標准的 Jackson 有條件地跳過對象進行序列化。 鏈接: https : //www.baeldung.com/jackson-serialize-field-custom-criteria

注意:我可以在沒有過濾器的情況下做到這一點。

所以我基本上在做什么,檢查一個屬性,如果它沒有設置,我跳過返回整個對象。 所以我得到了什么(基於教程示例):

HTTP: 200
PATH: /users
RESPONSE:
[
    {
        "name":"john"
    },
    {
        "name":"adam",
        "address":{
            "city":"ny",
            "country":"usa"
        }
    }
]

但是如果我們遇到一個案例返回一個隱藏的單個用戶,我們將面臨這樣的問題:

HTTP: 200
PATH: /users/tom
RESPONSE: /**empty response**/

在這種特殊情況下,我想返回一個帶有 404 錯誤代碼的 HTTP 響應,而不是像 jackson 那樣的 200。 調試時,Jackson 在控制器之后序列化,所以我無法攔截它。

我正在考慮實現攔截器,它可以攔截傑克遜響應編寫器,然后如果為空,則返回 404 錯誤代碼。

...我沒有想法,經驗泄漏。 :/
有誰知道如何做到這一點?

編輯::20200323

按照@Tomoki_Sato 的回答,我找到了解決方案。 在嘗試了他的回答后,它首先不起作用。 經過調查,問題出在類型不匹配上。
在我的控制器中,我總是返回ResponseEntity<?> ,它沒有實現Hideable類。

所以我的解決方案就是這樣,支持ResponseEntity<<? implements Hideable>> ResponseEntity<<? implements Hideable>> && <? implements Hideable> <? implements Hideable>響應:

@RestControllerAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice<Hideable> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        //if returnType is  <? implements hideable>
        if (Hideable.class.isAssignableFrom(returnType.getParameterType()) && MappingJackson2HttpMessageConverter.class.isAssignableFrom(converterType)) {
            return true;
        }

        //if returnType is  ResponseEntity<<? implements hideable>>
        List<Type> actualTypeArguments = Lists.newArrayList(((ParameterizedType) returnType.getGenericParameterType()).getActualTypeArguments());
        if (actualTypeArguments.isEmpty()) {
            return false;
        }

        try {
            Class<?> responseClass = Class.forName(actualTypeArguments.get(0).getTypeName());
            return Hideable.class.isAssignableFrom(responseClass) && MappingJackson2HttpMessageConverter.class.isAssignableFrom(converterType);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public Hideable beforeBodyWrite(
        Hideable hideable, MethodParameter returnType, MediaType selectedContentType,
        Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
        ServerHttpResponse response
    ) {

        if (hideable == null || hideable.isRemoved()) {
            response.setStatusCode(HttpStatus.NOT_FOUND);
            return null;
        }
        return hideable;
    }
}

談到效率,我沒有測試過,我相信我們必須在許多類型上測試它,比如ResponseEntity<List<? implements Hideable>> ResponseEntity<List<? implements Hideable>> , ResponseEntity<Set<? implements Hideable>> ResponseEntity<Set<? implements Hideable>> ....

在理論上,我相信@RestControllerAdvice不會在這里干擾,並且 JSON 序列化器正在率先轉換響應......我不知道。

我希望這對其他人有幫助:)

您可以通過實現ResponseBodyAdvice在 Jackson 編寫響應之前自定義ResponseBodyAdvice
如果您想在user為 null 或hidden時設置404 HTTP 狀態代碼,您的ResponseBodyAdvice實現將是這樣的:

@ControllerAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice<Hidable> {

    /**
     * Supports `? extends Hidable`, `ResponseEntity<? extends Hidable>` and
     * `HttpEntity<? extends Hidable>` handled by
     * `MappingJackson2HttpMessageConverter`
     */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {

        if (!MappingJackson2HttpMessageConverter.class.isAssignableFrom(converterType)) {
            return false;
        }

        Class<?> parameterType = returnType.getParameterType();

        // if returnType is <? extends Hidable>
        if (Hidable.class.isAssignableFrom(parameterType)) {
            return true;
        }

        // if returnType is ResponseEntity<? extends Hidable> or HttpEntity<? extends
        // Hidable>
        if (HttpEntity.class.isAssignableFrom(parameterType)) {

            Type[] actualTypeArguments = ((ParameterizedType) returnType.getGenericParameterType())
                    .getActualTypeArguments();
            if (actualTypeArguments == null || actualTypeArguments.length != 1) {
                return false;
            }
            try {
                return Hidable.class.isAssignableFrom(Class.forName(actualTypeArguments[0].getTypeName()));
            } catch (ClassNotFoundException e) {
                // e.g. returnType is ResponseEntity<List<Hideable>>
                e.printStackTrace();
            }

        }

        return false;
    }

    @Override
    public Hidable beforeBodyWrite(Hidable hidable, MethodParameter returnType, MediaType selectedContentType,
            Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
            ServerHttpResponse response) {

        if (hidable == null || hidable.isHidden()) {
            response.setStatusCode(HttpStatus.NOT_FOUND);
            return null;
        }
        return hidable;
    }
}

編輯::20200324

我根據上面EDIT::20200323 中的代碼片段改進了我的答案,以便ResponseBodyAdvice不僅可以支持Hidable還可以支持ResponseEntityHttpEntity
我想建議您檢查HttpEntityResponseEntity的超類)是否可以從parameterType分配,以便您可以防止您的ResponseBodyAdvice支持意外的參數類型,如List<Hidable> 如果ResponseBodyAdvice支持List<Hidable>ClassCastException發生在beforeBodyWrite

也可以看看
Spring 框架文檔 - Web on Servlet Stack - 1.1.6。 攔截
ResponseBodyAdvice 的 Java 文檔

暫無
暫無

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

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