Spring MVC:如何修改从控制器发送的json响应

我用这样的控制器构建了一个json REST服务:

@Controller @RequestMapping(value = "/scripts") public class ScriptController { @Autowired private ScriptService scriptService; @RequestMapping(method = RequestMethod.GET) @ResponseBody public List 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 } 

我遇到过类似的问题,建议您使用Servletfilter来解决它。

Servletfilter是可用于Servlet编程的Java类,用于在客户端访问后端资源之前拦截客户端的请求,或者在将服务器发送回客户端之前操纵服务器的响应。

您的filter必须实现javax.servlet.Filter接口并覆盖三个方法:

 public void doFilter (ServletRequest, ServletResponse, FilterChain) 

每次请求/响应对通过链时都会调用此方法,因为客户端请求链末端的资源。

 public void init(FilterConfig filterConfig) 

在filter投入使用之前调用,并设置filter的配置对象。

 public void destroy() 

在filter停止服务后调用。

可以使用任意数量的filter,执行顺序与web.xml中定义的顺序相同。

web.xml中:

 ...  restResponseFilter  com.package.filters.ResponseFilter    anotherFilter  com.package.filters.AnotherFilter   ... 

因此,此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)方法调用链中的下一个filter,或者如果调用filter是链中的最后一个filter,则调用servlet逻辑。

HTTP servlet响应包装器使用自定义servlet输出流,使得包装器在servlet完成写入后操作响应数据。 通常,在关闭servlet输出流之后不能这样做(实际上,在servlet提交之后)。 这就是为ServletOutputStream类实现特定于filter的扩展的原因。

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或更高版本,则可以在写入正文之前使用ResponseBodyAdivce自定义响应。