我用这样的控制器构建了一个 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();
}
}
它工作正常,但现在我需要修改所有响应并向所有响应添加“状态”和“消息”字段。我已经阅读了一些解决方案:
- 从某些特定类的所有控制器方法对象返回,例如,RestResponse,它将包含“状态”和“消息”字段(但这不是通用解决方案,因为我将不得不修改所有控制器并以新样式编写新控制器)
- 拦截所有带有方面的控制器方法(但在这种情况下,我无法更改返回类型)
如果我想将从控制器方法返回的值包装到类的对象中,您能否提出其他一些通用且正确的解决方案:
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 中定义的顺序相同。
网页.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,将 as 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(); } }
我认为这种方法将是您问题的一个很好的解决方案。
如果有什么不明白的地方,请提出问题,如果我错了,请纠正我。