Spring MVC:如何修改从控制器发送的 json 响应 [英] Spring MVC: How to modify json response sent from controller

查看:34
本文介绍了Spring MVC:如何修改从控制器发送的 json 响应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've built a json REST service with controllers like this one:

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

    @Autowired
    private ScriptService scriptService;

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

它工作正常,但现在我需要修改所有响应并向所有响应添加状态"和消息"字段.我已经阅读了一些解决方案:

It works fine, but now I need to modify all responses and add "status" and "message" fields to all of them. I've read about some solutions:

  1. 从某个特定类的所有控制器方法对象返回,例如,RestResponse,它将包含状态"和消息"字段(但它是不是通用的解决方案,因为我将不得不修改我所有的控制器并以新风格编写新控制器)
  2. 拦截所有带有切面的控制器方法(但在这种情况下我无法更改返回类型)

如果我想将从控制器方法返回的值包装到类的对象中,您能否提出其他一些通用且正确的解决方案:

Can you suggest some other, general and correct solution, if I want to wrap values returned from controller methods into objects of class:

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 Filters 来解决.

I've encountered with similar problem and suggest you to use Servlet Filters to resolve it.

Servlet 过滤器是 Java 类,可在 Servlet 编程中用于在客户端访问后端资源之前拦截来自客户端的请求,或者在将响应发送回客户端之前操作来自服务器的响应.

Servlet Filters are Java classes that can be used in Servlet Programming to intercept requests from a client before they access a resource at back end or to manipulate responses from server before they are sent back to the client.

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

Your filter must implement the javax.servlet.Filter interface and override three methods:

public void doFilter (ServletRequest, ServletResponse, FilterChain)

每次请求/响应对通过链时都会调用此方法,这是由于客户端对链末端资源的请求.

This method is called every time a request/response pair is passed through the chain due to a client request for a resource at the end of the chain.

public void init(FilterConfig filterConfig)

在过滤器投入使用之前调用,并设置过滤器的配置对象.

Called before the filter goes into service, and sets the filter's configuration object.

public void destroy()

在过滤器停止服务后调用.

Called after the filter has been taken out of service.

可以使用任意数量的过滤器,执行顺序将与它们在 web.xml 中定义的顺序相同.

There is possibility to use any number of filters, and the order of execution will be the same as the order in which they are defined in the 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>
...

因此,此过滤器获取控制器响应,将其转换为字符串,将 as feild 添加到您的 RestResponse 类对象(带有状态和消息字段),将其对象序列化为 Json 并将完整的响应发送给客户端.

So, this filter gets the controller response, converts it into String, adds as feild to your RestResponse class object (with status and message fields), serializes it object into Json and sends the complete response to the client.

ResponseFilter 类:

ResponseFilter class:

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 逻辑.

chain.doFilter(request, responseWrapper) method invokes the next filter in the chain, or if the calling filter is the last filter in the chain invokes servlet logic.

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

The HTTP servlet response wrapper uses a custom servlet output stream that lets the wrapper manipulate the response data after the servlet is finished writing it out. Normally, this cannot be done after the servlet output stream has been closed (essentially, after the servlet has committed it). That is the reason for implementing a filter-specific extension to the ServletOutputStream class.

FilterServletOutputStream 类:

FilterServletOutputStream class:

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 生成的原始响应.

To use the FilterServletOutputStream class should be implemented a class that can act as a response object. This wrapper object is sent back to the client in place of the original response generated by the servlet.

ResponseWrapper 类:

ResponseWrapper class:

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

我认为这种方法可以很好地解决您的问题.

I think this approach will be a good solution for your issue.

如果有什么不明白的地方,请提出问题,如果我错了,请纠正我.

Please, ask a questions, if something not clear and correct me if I'm wrong.

这篇关于Spring MVC:如何修改从控制器发送的 json 响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆