Изменить HTTP-код, когда Serialize пропускает объекты условно, которые соответствуют пользовательским критериям с Джексоном (Spring boot) - PullRequest
1 голос
/ 22 марта 2020

Таким образом, я смог условно сериализовать пропущенные объекты, которые соответствуют пользовательским критериям, с Джексоном по указанной ссылке ссылка: 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**/

В этом конкретном случае я хочу вернуть HTTP-ответ с кодом ошибки 404, а не 200, как ведет себя Джексон. при отладке Джексон выполняет сериализацию после контроллера, поэтому я не мог его перехватить.

Я думал о реализации перехватчиков, которые могли бы перехватывать устройство записи ответов Джексона, а затем, если оно пустое, возвращать код ошибки 404.

... У меня нет идей и нет опыта. : /
Кто-нибудь знает, как это сделать?

РЕДАКТИРОВАТЬ :: 20200323

после ответа @Tomoki_Sato, я нашел решение. Попробовав его ответ, он не сработал первым. После исследования проблема была связана с несовпадением типов.
В моем контроллере я всегда возвращаю ResponseEntity<?>, который не реализует класс Hideable.

Так что мое решение было таким, поддерживающим ResponseEntity<<? 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<Set<? implements Hideable>> ... .

В теории я считаю, что @RestControllerAdvice здесь не вмешивается, и сериализатор JSON играет ведущую роль в преобразовании ответа ... я не знаю.

Я надеюсь, что это помогает кому-то еще:)

1 Ответ

2 голосов
/ 22 марта 2020

Вы можете настроить ответ до того, как Джексон его напишет, реализовав ResponseBodyAdvice.
Если вы хотите установить код состояния 404 HTTP, когда user равен нулю или hidden, ваша реализация 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

Я улучшил свой ответ на основе фрагмента кода в РЕДАКТИРОВАТЬ :: 20200323 выше, чтобы ResponseBodyAdvice мог поддерживать не только Hidable, но также ResponseEntity и HttpEntity.
. Я хотел бы предложить вам проверить, является ли HttpEntity (суперкласс ResponseEntity) присваивается с parameterType, так что вы можете запретить вашему ResponseBodyAdvice поддерживать неожиданные типы параметров, такие как List<Hidable>. Если ResponseBodyAdvice поддерживает List<Hidable>, ClassCastException происходит в beforeBodyWrite.

См. Также
Документация Spring Framework - Web в стеке сервлетов - 1.1.6. Перехват
Java do c ResponseBodyAdvice

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...