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

2024-01-20

我已经使用如下控制器构建了一个 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();
    }
}

它工作正常,但现在我需要修改所有响应并向所有响应添加“状态”和“消息”字段。我读过一些解决方案:

  1. 从某个特定类的所有控制器方法对象返回,例如, RestResponse,它将包含“状态”和“消息”字段(但它是 不是通用的解决方案,因为我必须修改所有控制器 并以新的风格编写新的控制器)
  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 Filters 来解决它。

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 并将完整响应发送到客户端。

响应过滤器类:

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 类实现特定于过滤器的扩展的原因。

过滤ServletOutputStream类:

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 生成的原始响应发送回客户端。

响应包装类:

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

我认为这种方法将很好地解决您的问题。

如果有不清楚的地方,请提出问题,如果我错了,请纠正我。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • Docker for Windows - 访问本地网络中的容器

    我已经安装了适用于 Windows 的 Docker 并在其上运行 Nexus Repository Manager 容器 现在我想让我的 Nexus 容器可以从内部网络中的其他电脑访问 怎么做 您必须将端口映射到容器 端口 443 的示例
  • Meteor:ReferenceError:帐户未定义

    我刚刚完成了 Meteor 包的开发 现在我想通过将其添加到新的 Meteor 应用程序来测试它 my cool package name package js Package on use function api api use ema
  • 服务器已经在运行。检查…/tmp/pids/server.pid。退出 - 轨道

    rails s gt Booting WEBrick gt Rails 4 0 4 application starting in development on http 0 0 0 0 3000 gt Run rails server h
  • 带参数的 RelayCommand 抛出 MethodAccessException

    我正在使用 Net 和 MVVM Light 创建应用程序 但 RelayCommands 遇到一些问题 我正在尝试创建一个 RelayCommand 它接受一个参数并将其传递给同一 ViewModel 中的函数 然而 每次我尝试这样做时
  • grails 3 中的外部属性文件

    我需要从 grails 3 中的外部文件属性读取配置 在 grails 2 x 中 我将文件链接到 grails config locations classpath config properties 在config groovy中 但是
  • 如何以这种特定方式拆分 git commit

    情况 我有一个git提交 在 HEAD 处 其中混合了额外的日志记录代码 然后是一些 真实代码 现在我想做以下事情 编辑掉所有日志代码 基本上清理了代码库 Commit this 我现在在最后两次提交中拥有所需的状态 但它们的顺序错误 首先
  • 用于按分隔符分割字符串的 mySQL 存储过程

    我正在编写一个存储过程 它将传递的字符串分解为 传递分隔符并返回结果的第 n 个元素 n 已通过 也 所以这就是我想出的 CREATE PROCEDURE SPLIT IN strToSplit text IN strDelimiter v
  • 调整具有宽度限制的框架大小

    我有简单的形式TForm1有 2 个面板 首先与Align alLeft第二个是Align alClient和空框TFrame1 当我将以下过程添加到表单中时 一切正常 procedure TForm1 FormCreate Sender
  • 禁用链接以停止 JQuery 中的双击

    我如何禁用所有链接button点击一次后上课 我希望能够在一个地方完成此操作 而不必单独更改所有这些 有什么想法吗 到目前为止我得到了这个 a button click function this attr disabled disable
  • 使用指向非静态成员函数的指针实现回调

    假设我正在开发一个杂货清单管理器 我有一扇窗户 上面有GroceryListDisplay 这是一个显示购物清单上的商品的控件 杂货数据由程序的模型组件存储在GroceryStorage class 要将保存的文件加载到我的程序中 必须使用
  • Flutter SharedPreferences如何加载所有保存的?

    如何加载 SharedPreferences 中保存的所有内容 我保存了很多布尔值 需要将所有布尔值加载到列表中 这就是我保存的方式 SharedPreferences sharedPreferences bool isfavorit ov
  • T-SQL 分割字符串

    我有一个 SQL Server 2008 R2 列 其中包含一个需要用逗号分隔的字符串 我在 StackOverflow 上看到了很多答案 但没有一个在 R2 中有效 我已确保我对任何拆分函数示例具有选择权限 非常感谢任何帮助 我之前用过这
  • R中Box Cox变换故障排除(需要使用for循环或apply)

    请在下面找到我的数据 行是疾病组 0 对照 1 溃疡性结肠炎和 2 克罗恩病 列是基因表达值 structure c 5 54312e 05 5 6112e 06 9 74312e 05 1 3612e 06 1 29312e 05 7 2
  • R 中 nlme 线性混合模型中相互作用显着性的检验

    I use lme函数在nlme用于测试因子水平的 R 包items与因子水平有显着的交互作用condition 因素condition有两个级别 Control and Treatment 以及因子items有 3 个级别 E1 E3 我
  • 如何在ubuntu-18.04上安装nexus

    我需要帮助在 ubuntu 18 04 上安装 nexus oss 我在互联网上找不到任何 apt get 命令 我尝试在 sudo apt get search nexus 中搜索nexus包 但无法获得正确的nexus版本包 我在网上浏
  • Bootstrap Affix 插件内存泄漏

    这些行 https github com twbs bootstrap blob master js affix js L19 L21在引导程序词缀插件中似乎会导致内存泄漏 因为窗口获取对从未释放的词缀实例的引用 作为解决方法 我使用此代码
  • OpenAPI 生成器的 Gradle 配置

    当将 OpenAPI 生成器与 Gradle 一起使用时 我希望将性别源发送到其他源生成器插件使用的标准目录 类似 Maven 生成源的东西 到目前为止 我还无法做到这一点 特别是限制生成 Java 源类而不是整个 原型项目 看来 Open
  • 在 AVX 寄存器内循环字节的有效方法

    摘要 tl 博士 除了进行 2 倍移位并将结果混合在一起之外 还有什么方法可以按位旋转 YMM 寄存器中的字节 使用 AVX 对于 YMM 寄存器中的每 8 个字节 我需要向左旋转 7 个字节 每个字节都需要比前一个字节向左旋转一位 因此
  • 如何为 SASS 变量中的 fs-* 类自定义 Bootstrap 5 字体大小

    如何更改 Bootstrap 5fs 上课于sass 因为在文档 https getbootstrap com docs 5 0 utilities text variables仅显示如何修改h 类如h5 font size 但不是fs c
  • Spring MVC:如何修改从控制器发送的json响应

    我已经使用如下控制器构建了一个 json REST 服务 Controller RequestMapping value scripts public class ScriptController Autowired private Scr