복붙노트

[SPRING] 스프링에서 JSON 응답을 래핑하는 방법은 무엇입니까?

SPRING

스프링에서 JSON 응답을 래핑하는 방법은 무엇입니까?

스프링에 두 세트의 컨트롤러가 있다고 가정합니다.

둘 다 JSON 텍스트로 해석 될 객체를 반환합니다.

이러한 종류의 컨트롤러에서 응답을 래핑하는 일종의 필터가 필요합니다.

위의 작업을 수행하기 위해 WebMvcConfigurerAdapter.configureMessageConverters에 전달 된 MappingJackson2HttpMessageConverter를 현재 사용자 지정하고 있습니다. 이 접근법이 적용되는 URL (또는 컨트롤러 클래스)에 대해 선택적인 것으로 보이지 않는 것을 제외하고는 훌륭하게 작동합니다.

이러한 종류의 래퍼를 개별 컨트롤러 클래스 나 URL에 적용 할 수 있습니까?

업데이트 : 서블릿 필터는 솔루션처럼 보입니다. 어떤 필터가 어떤 컨트롤러 메소드 또는 어떤 URL에 적용되는지 선택할 수 있습니까?

해결법

  1. ==============================

    1.내가 당신의 질문을 이해하는 방식으로, 정확히 세 가지 선택이 있습니다.

    내가 당신의 질문을 이해하는 방식으로, 정확히 세 가지 선택이 있습니다.

    옵션 1

    필요한 필드가있는 간단한 SuccessResponse, ErrorResponse, SomethingSortOfWrongResponse 등의 개체에 개체를 수동으로 래핑하십시오. 이 시점에서 응답 요청자의 유연성을 확보 할 수 있습니다. 응답 래퍼 중 하나의 필드를 변경하는 것은 간단하며 많은 컨트롤러 요청 메서드를 함께 그룹화 할 수 있고 함께 그룹화해야하는 경우 코드 반복이 유일한 단점입니다.

    옵션 # 2

    언급했듯이 필터는 더러운 작업을하도록 설계 될 수 있지만 Spring 필터는 요청 또는 응답 데이터에 대한 액세스 권한을 부여하지 않습니다. 다음과 같은 모양의 예가 있습니다.

    @Component
    public class ResponseWrappingFilter extends GenericFilterBean {
    
        @Override
        public void doFilter(
            ServletRequest request,
            ServletResponse response,
            FilterChain chain) {
    
            // Perform the rest of the chain, populating the response.
            chain.doFilter(request, response);
    
            // No way to read the body from the response here. getBody() doesn't exist.
            response.setBody(new ResponseWrapper(response.getStatus(), response.getBody());
        }
    } 
    

    해당 필터에서 본문을 설정하는 방법을 찾으면 네, 쉽게 마무리 할 수 ​​있습니다. 그렇지 않으면이 옵션은 막 다른 길입니다.

    옵션 # 3

    아하. 그래서 너는 이걸 가지고있어. 코드 중복은 옵션이 아니지만 컨트롤러 메소드의 응답을 래핑하는 것이 좋습니다. Spring이 좋아하게 지원하는 AOP (aspect-oriented programming)라는 진정한 솔루션을 소개하고자합니다.

    AOP에 익숙하지 않다면 전제는 다음과 같습니다 : 정규 표현식과 일치하는 표현식을 코드에서 정의하십시오. 이 점을 결합 점이라고 부르며, 일치하는 표현식을 점 점 (pointcut)이라고합니다. 그런 다음 pointcut 또는 pointcuts 조합이 일치하면 advice라고하는 추가 임의 코드를 실행할 수 있습니다. pointcuts와 조언을 정의하는 객체를 aspect라고 부릅니다.

    Java에서 더 유창하게 자신을 표현하는 데 좋습니다. 유일한 단점은 더 약한 정적 유형 검사입니다. 더 이상 신경 쓸 필요없이 aspect 지향 프로그래밍에서의 응답 포장은 다음과 같습니다.

    @Aspect
    @Component
    public class ResponseWrappingAspect {
    
        @Pointcut("within(@org.springframework.stereotype.Controller *)")
        public void anyControllerPointcut() {}
    
        @Pointcut("execution(* *(..))")
        public void anyMethodPointcut() {}
    
        @AfterReturning(
            value = "anyControllerPointcut() && anyMethodPointcut()",
            returning = "response")
        public Object wrapResponse(Object response) {
    
            // Do whatever logic needs to be done to wrap it correctly.
            return new ResponseWrapper(response);
        }
    
        @AfterThrowing(
            value = "anyControllerPointcut() && anyMethodPointcut()",
            throwing = "cause")
        public Object wrapException(Exception cause) {
    
            // Do whatever logic needs to be done to wrap it correctly.
            return new ErrorResponseWrapper(cause);
        }
    }
    

    최종 결과는 사용자가 추구하는 반복되지 않는 응답 배치입니다. 하나 또는 그 이상의 컨트롤러가이 효과를 받기를 원할 경우, (@Controller 어노테이션이있는 클래스가 아닌) 해당 컨트롤러의 인스턴스 내에서만 메소드와 일치하도록 pointcut을 업데이트하십시오.

    AOP 의존성을 포함하고, 구성 클래스에 AOP 사용 가능 주석을 추가하고, 구성 요소가이 클래스가있는 패키지를 스캔하는지 확인해야합니다.

  2. ==============================

    2.나는 이것을 여러 날 동안 고민하고있었습니다. @Misha의 해결책은 저에게 효과적이지 않았습니다. 나는 ControllerAdvice와 ResponseBodyAdvice를 사용하여이 작업을 마침내 얻을 수있었습니다.

    나는 이것을 여러 날 동안 고민하고있었습니다. @Misha의 해결책은 저에게 효과적이지 않았습니다. 나는 ControllerAdvice와 ResponseBodyAdvice를 사용하여이 작업을 마침내 얻을 수있었습니다.

    ResponseBodyAdvice를 사용하면 컨트롤러가 반환하지만 HttpResponse로 변환되어 커밋되기 전에 사용자 지정 변환 논리를 응답에 삽입 할 수 있습니다.

    이것이 내 컨트롤러 메소드의 모습입니다.

    @RequestMapping("/global/hallOfFame")
        public List<HallOfFame> getAllHallOfFame() {
            return hallOfFameService.getAllHallOfFame();
    }
    

    이제는 devmessage 및 usermessage와 같은 응답에 몇 가지 표준 필드를 추가하려고했습니다. 그 논리는 ResponseAdvice에 들어갑니다.

    @ControllerAdvice
    public class TLResponseAdvice implements ResponseBodyAdvice<Object> {
    
        @Override
        public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
            return true;
        }
    
        @Override
        public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
            ServerHttpResponse response) {
            // TODO Auto-generated method stub
            final RestResponse<Object> output = new RestResponse<>();
            output.setData(body);
            output.setDevMessage("ResponseAdviceDevMessage");
            output.setHttpcode(200);
            output.setStatus("Success");
            output.setUserMessage("ResponseAdviceUserMessage");
            return output;
        }
    }
    

    엔티티 클래스는 다음과 같습니다.

    @Setter // All lombok annotations
    @Getter
    @ToString
    public class RestResponse<T> {
    
        private String status;
        private int httpcode;
        private String devMessage;
        private String userMessage;
    
        private T data;
    }
    
    @Entity
    @Data // Lombok
    public class HallOfFame {
    
        @Id
        private String id;
        private String name;
    }
    

    예외를 처리하려면 ExceptionHandler를 사용하여 다른 ControllerAdvice를 작성하기 만하면됩니다. 이 링크의 예제를 사용하십시오.

    이 솔루션의 장점 :

  3. ==============================

    3.컨트롤러에서 사용자 지정 응답을 관리하는 가장 간단한 방법은 Map 변수를 사용하는 것입니다.

    컨트롤러에서 사용자 지정 응답을 관리하는 가장 간단한 방법은 Map 변수를 사용하는 것입니다.

    그래서 코드는 다음과 같이 보입니다.

    public @ResponseBody Map controllerName(...) {
    
    Map mapA = new HashMap();
    
    mapA.put("status", "5");
    mapA.put("content", "something went south");
    
    return mapA;
    
    }
    

    아름다움은 당신이 그것을 천 가지 방법으로 구성 할 수 있다는 것입니다. 현재 개체 전송, 사용자 지정 예외 처리 및 데이터보고에 너무 쉽게 사용됩니다.

    희망이 도움이

  4. ==============================

    4.나는 또한 AOP와 함께 @Around를 사용하고있다. 사용자 정의 주석을 개발하고이를 포인트 컷에 사용합니다. 나는 글로벌 응답을 사용하고있다. 그것은 유형의 목록 유형 인 메시지 및 데이터 상태를 갖습니다

    나는 또한 AOP와 함께 @Around를 사용하고있다. 사용자 정의 주석을 개발하고이를 포인트 컷에 사용합니다. 나는 글로벌 응답을 사용하고있다. 그것은 유형의 목록 유형 인 메시지 및 데이터 상태를 갖습니다

    List <? extends parent> dataList
    

    (귀하의 클래스 캐스트 예외를 해결할 수 있습니다). 모든 엔티티는이 Parent 클래스를 확장합니다. 이 방법으로 모든 데이터를 내 목록으로 설정할 수 있습니다. 또한 사용자 지정 주석을 사용하여 메시지 키를 param으로 사용하고 실제로 설정합니다.  희망이 도움이됩니다.

  5. from https://stackoverflow.com/questions/24072458/how-can-i-wrap-a-json-response-in-spring by cc-by-sa and MIT license