복붙노트

[SPRING] Spring MVC : 컨트롤러에서 보낸 json 응답을 수정하는 방법

SPRING

Spring MVC : 컨트롤러에서 보낸 json 응답을 수정하는 방법

나는 이것과 같은 컨트롤러로 json REST 서비스를 만들었다 :

@Controller
@RequestMapping(value = "/scripts")
public class ScriptController {

    @Autowired
    private ScriptService scriptService;

    @RequestMapping(method = RequestMethod.GET)
    @ResponseBody
    public List<Script> get() {
        return scriptService.getScripts();
    }
}

그것은 잘 작동하지만 지금 모든 응답을 수정하고 모든 "상태"및 "메시지"필드를 추가해야합니다. 몇 가지 해결책을 읽었습니다.

컨트롤러 메서드에서 반환 된 값을 클래스의 객체로 래핑하려는 경우 다른 일반적이고 올바른 해결책을 제안 할 수 있습니까?

public class RestResponse {

    private int status;
    private String message;
    private Object data;

    public RestResponse(int status, String message, Object data) {
        this.status = status;
        this.message = message;
        this.data = data;
    }

    //getters and setters
}

해결법

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

    1.유사한 문제가 발생하여 서블릿 필터를 사용하여 문제를 해결할 것을 제안합니다.

    유사한 문제가 발생하여 서블릿 필터를 사용하여 문제를 해결할 것을 제안합니다.

    서블릿 필터는 서블릿 프로그래밍에서 백엔드의 리소스에 액세스하기 전에 클라이언트의 요청을 가로 채거나 서버에서 응답을 처리하여 클라이언트로 다시 보내기 전에 사용할 수있는 Java 클래스입니다.

    필터는 javax.servlet.Filter 인터페이스를 구현하고 세 가지 메소드를 대체해야합니다.

    public void doFilter (ServletRequest, ServletResponse, FilterChain)
    

    이 메소드는 체인의 끝에있는 자원에 대한 클라이언트 요청으로 인해 요청 / 응답 쌍이 체인을 통해 전달 될 때마다 호출됩니다.

    public void init(FilterConfig filterConfig)
    

    필터가 사용되기 전에 호출되어 필터의 구성 객체를 설정합니다.

    public void destroy()
    

    필터가 서비스 종료 된 후에 불려갑니다.

    여러 개의 필터를 사용할 수 있으며 실행 순서는 web.xml에 정의 된 순서와 동일합니다.

    веб.хмл :

    ...
    <filter>
        <filter-name>restResponseFilter</filter-name>
        <filter-class>
            com.package.filters.ResponseFilter
        </filter-class>
    </filter>
    
    <filter>
        <filter-name>anotherFilter</filter-name>
        <filter-class>
            com.package.filters.AnotherFilter
        </filter-class>
    </filter>
    ...
    

    따라서이 필터는 컨트롤러 응답을 가져 와서 String으로 변환하고 RestResponse 클래스 객체 (상태 및 메시지 필드 포함)에 feild로 추가하고 객체를 Json으로 직렬화 한 다음 클라이언트에 전체 응답을 보냅니다.

    ResponseFilter 클래스 :

    public final class ResponseFilter implements Filter {
    
    @Override
        public void init(FilterConfig filterConfig) {
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
    
        ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse) response);
    
        chain.doFilter(request, responseWrapper);
    
        String responseContent = new String(responseWrapper.getDataStream());
    
        RestResponse fullResponse = new RestResponse(/*status*/, /*message*/,responseContent);
    
        byte[] responseToSend = restResponseBytes(fullResponse);
    
        response.getOutputStream().write(responseToSend);
    
    }
    
    @Override
    public void destroy() {
    }
    
    private byte[] restResponseBytes(RestResponse response) throws IOException {
        String serialized = new ObjectMapper().writeValueAsString(response);
        return serialized.getBytes();
    }
    }
    

    chain.doFilter (request, responseWrapper) 메소드는 체인의 다음 필터를 호출하거나 호출 필터가 체인의 마지막 필터 인 경우 서블릿 논리를 호출합니다.

    HTTP 서블릿 응답 랩퍼는 사용자 정의 서블릿 출력 스트림을 사용하여 서블릿 작성이 완료된 후 랩퍼가 응답 데이터를 조작하게합니다. 일반적으로 서블릿 출력 스트림이 닫힌 후에는 (기본적으로 서블릿이 서블릿을 커밋 한 후에)이를 수행 할 수 없습니다. 이것이 ServletOutputStream 클래스에 대한 필터 관련 확장을 구현하는 이유입니다.

    FilterServletOutputStream 클래스 :

    public class FilterServletOutputStream extends ServletOutputStream {
    
    DataOutputStream output;
    public FilterServletOutputStream(OutputStream output) {
        this.output = new DataOutputStream(output);
    }
    
    @Override
    public void write(int arg0) throws IOException {
        output.write(arg0);
    }
    
    @Override
    public void write(byte[] arg0, int arg1, int arg2) throws IOException {
        output.write(arg0, arg1, arg2);
    }
    
    @Override
    public void write(byte[] arg0) throws IOException {
        output.write(arg0);
    }
    }
    

    FilterServletOutputStream 클래스를 사용하려면 응답 객체로 사용할 수있는 클래스를 구현해야합니다. 이 래퍼 객체는 서블릿에 의해 생성 된 원래 응답 대신 클라이언트에 다시 전송됩니다.

    ResponseWrapper 클래스 :

    public class ResponseWrapper extends HttpServletResponseWrapper {
    
    ByteArrayOutputStream output;
    FilterServletOutputStream filterOutput;
    HttpResponseStatus status = HttpResponseStatus.OK;
    
    public ResponseWrapper(HttpServletResponse response) {
        super(response);
        output = new ByteArrayOutputStream();
    }
    
    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        if (filterOutput == null) {
            filterOutput = new FilterServletOutputStream(output);
        }
        return filterOutput;
    }
    
    public byte[] getDataStream() {
        return output.toByteArray();
    }
    }
    

    나는이 접근법이 당신의 문제에 대한 좋은 해결책이 될 것이라고 생각합니다.

    제가 틀렸다면, 명확하지 않고 뭔가를 수정 해 주시길 바랍니다.

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

    2.Spring 4.1 이상을 사용하는 경우 ResponseBodyAdivce를 사용하여 사용자 정의 할 수 있습니다 신체가 쓰여지기 전에 응답.

    Spring 4.1 이상을 사용하는 경우 ResponseBodyAdivce를 사용하여 사용자 정의 할 수 있습니다 신체가 쓰여지기 전에 응답.

  3. from https://stackoverflow.com/questions/25020331/spring-mvc-how-to-modify-json-response-sent-from-controller by cc-by-sa and MIT license