簡體   English   中英

Spring MVC:如何修改從控制器發送的json響應

[英]Spring MVC: How to modify json response sent from controller

我用這樣的控制器構建了一個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();
    }
}

它工作正常,但現在我需要修改所有響應並向所有響應添加“status”和“message”字段。 我讀過一些解決方案:

  1. 從一些特定類的所有控制器方法對象返回,例如,RestResponse,它將包含“status”和“message”字段(但它不是一般的解決方案,因為我將不得不修改我的所有控制器並以新的方式編寫新的控制器)
  2. 攔截所有控制器方法與方面(但在這種情況下我不能改變返回類型)

如果我想將從控制器方法返回的值包裝到類的對象中,您能否提出一些其他的,一般的和正確的解決方案:

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
}

我遇到過類似的問題,建議您使用Servlet過濾器來解決它。

Servlet過濾器是Java類,可以在Servlet編程中用於在客戶端訪問后端資源之前攔截客戶端的請求,或者在將服務器發送回客戶端之前操縱服務器的響應。

您的過濾器必須實現javax.servlet.Filter接口並覆蓋三個方法:

public void doFilter (ServletRequest, ServletResponse, FilterChain)

每次請求/響應對通過鏈時都會調用此方法,因為客戶端請求鏈末端的資源。

public void init(FilterConfig filterConfig)

在過濾器投入使用之前調用,並設置過濾器的配置對象。

public void destroy()

在過濾器停止服務后調用。

可以使用任意數量的過濾器,執行順序與web.xml中定義的順序相同。

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,作為feild添加到您的RestResponse類對象(具有狀態和消息字段),將其對象序列化為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)方法調用鏈中的下一個過濾器,或者如果調用過濾器是鏈中的最后一個過濾器,則調用servlet邏輯。

HTTP servlet響應包裝器使用自定義servlet輸出流,使得包裝器在servlet完成寫入后操作響應數據。 通常,在關閉servlet輸出流之后無法完成此操作(實際上,在servlet提交之后)。 這就是為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類,應該實現一個可以充當響應對象的類。 此包裝器對象將發送回客戶端,代替servlet生成的原始響應。

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();
}
}

我認為這種方法對您的問題來說是一個很好的解決方案。

如果問題不清楚,請提出問題,如果我錯了,請糾正我。

如果使用spring 4.1或更高版本,則可以在寫入正文之前使用ResponseBodyAdvice來自定義響應。

暫無
暫無

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

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