Spring MVC 更灵活的控制 json 返回问题(自定义过滤字段)

yizhihongxing

Spring MVC 是一款常用的 Web 框架,用于开发 Java Web 应用程序。它允许开发者对应用程序做出灵活的控制,其中一项迫切需要的控制就是对返回 JSON 数据的过滤。本文将探讨如何通过 Spring MVC 实现更灵活的对 JSON 返回数据进行过滤的控制。

环境搭建

在本地安装好 JDK 1.8 和 Maven 3.x 后,在 pom.xml 文件中添加以下依赖:

<!-- Spring Web MVC -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.0</version>
</dependency>

<!-- Jackson JSON Processor -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.1</version>
</dependency>

创建控制器

创建一个 Spring MVC 的控制器类,并添加 @RestController@RequestMapping 注解。这里提供一个例子:

@RestController
@RequestMapping(value = "/example")
public class ExampleController {

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public ExampleData getById(@PathVariable String id) {
        return new ExampleData(id, "example data");
    }

    @RequestMapping(value = "/", method = RequestMethod.POST)
    public ExampleData create(@RequestBody ExampleData data) {
        data.setId(UUID.randomUUID().toString());
        return data;
    }

}

这里的 ExampleData 类是一个简单的 POJO,用于演示如何过滤掉返回的 JSON 数据中的部分属性:

public class ExampleData {

    private String id;
    private String name;

    public ExampleData(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

配置 Jackson

Spring MVC 使用 Jackson 作为 JSON 处理器,因此需要配置 ObjectMapper 及相关 JSON 消息转换器。你可以使用以下 Java 配置方式,也可以使用 XML 配置方式。

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = jacksonConverter.getObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        SimpleFilterProvider filterProvider = new SimpleFilterProvider().setFailOnUnknownId(false);
        filterProvider.addFilter("exampleDataFilter", new ExampleDataFilter());
        objectMapper.setFilterProvider(filterProvider);
        converters.add(jacksonConverter);
    }

}

这里使用的是 Java 配置方式。configureMessageConverters 方法会注册一个 MappingJackson2HttpMessageConverter 实例,其中通过对 ObjectMapper 进行设置,使得当 JSON 序列化为字符串时,能够过滤掉对象中的某些属性。具体来说,设置了:

  • 通过 setSerializationInclusion 方法将空值过滤掉;
  • 使用 SimpleFilterProvider 来添加自定义的过滤器。

为了能够让 ObjectMapper 能够识别到定义好的该过滤器,需要在调用 addFilter 方法时指定过滤器的名字,这个名字可以任意指定,但是在后面配置返回值过滤器时需要其与之一致。

利用方法 configureMessageConverters 注册后,Spring MVC 会在返回 JSON 响应时自动启用该 ObjectMappr 进行 JSON 序列化。

但是,在此之前,还需定义过滤器的具体操作,也即确定过滤掉哪些属性,看下一步。

编写自定义过滤器

定义一个过滤器类,在其中重写方法,实现修改要序列化的 Java 对象,去掉不想要返回的属性。代码如下:

public class ExampleDataFilter extends SimpleBeanPropertyFilter {

    @Override
    public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider provider,
                                 PropertyWriter writer) throws Exception {
        if (include(writer)) {
            super.serializeAsField(pojo, jgen, provider, writer);
        }
    }

    @Override
    protected boolean include(BeanPropertyWriter writer) {
        return true;
    }

    @Override
    protected boolean include(PropertyWriter writer) {
        return !writer.getName().equals("id");
    }

}

该类中只有两个重写的方法比较重要:serializeAsFieldinclude

  • serializeAsField: 将 Java 对象序列化为 JSON 对象时,给出写入的操作方式,默认用 Jackson 的默认方法写入域的名称和值。
  • include: 实现字段的过滤逻辑,当返回值的某个属性满足某个条件时,可以选择将其过滤掉。该方法采用了“模板设计模式”,即 include 方法调用子类的 include 方法来判断一个属性是否应当被序列化。

在这个例子中,ExampleDataFilter 过滤器中定义了,只要属性名字不是 “id” 就输出,否则跳过不输出。这就实现了过滤的操作。

配置返回值过滤器

回到 WebMvcConfig 配置。如果你想更精细地控制哪些接口中启用过滤器,那么更好的方式是使用 RequestMappingHandlerAdapter 的 MessageConverterConfigurer:

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        for (HttpMessageConverter<?> converter : converters) {
            if (converter instanceof MappingJackson2HttpMessageConverter) {
                MappingJackson2HttpMessageConverter jacksonConverter = (MappingJackson2HttpMessageConverter) converter;
                ObjectMapper objectMapper = jacksonConverter.getObjectMapper();
                objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
                SimpleFilterProvider filterProvider = new SimpleFilterProvider().setFailOnUnknownId(false);
                filterProvider.addFilter("exampleDataFilter", new ExampleDataFilter());
                objectMapper.setFilterProvider(filterProvider);
                jacksonConverter.setObjectMapper(objectMapper);
                MappingJackson2HttpMessageConverter responseConverter = new MappingJackson2HttpMessageConverter();
                jacksonConverter.setObjectMapper(responseConverter.getObjectMapper());
                List<MediaType> supportedMediaTypes = responseConverter.getSupportedMediaTypes();
                if (!supportedMediaTypes.contains(MediaType.APPLICATION_JSON)) {
                    supportedMediaTypes.add(MediaType.APPLICATION_JSON);
                }
                responseConverter.setSupportedMediaTypes(supportedMediaTypes);
                HttpMessageConverter<?> responseConverterProxy = new BeanNameAutoProxyCreator()
                        .createProxy(responseConverter, "jsonConverter");
                converters.add(0, responseConverterProxy);
            }
        }
    }

    @Bean
    public BeanNameAutoProxyCreator proxyCreator() {
        return new BeanNameAutoProxyCreator();
    }

    @Bean
    public JsonViewSupportFactoryBean jsonViewSupportFactoryBean() {
        return new JsonViewSupportFactoryBean();
    }
}

在上述代码中的 configureMessageConverters 中,注册了一个默认的 MappingJackson2HttpMessageConverter 进行 JSON 序列化。在这里,我们需要对默认的 MappingJackson2HttpMessageConverter 进行修改,添加新的序列化过滤器。

首先,查找当前请求需要使用的 MessageConverter 类型,如果该类型为 MappingJackson2HttpMessageConverter,则针对该转换器做以下修改:

  1. 获取该转换器的 ObjectMapper 对象,默认设置为排除 null 的空对象;
  2. 为该 ObjectMapper 对象添加新的过滤器;
  3. 重新配置 MappingJackson2HttpMessageConverter 对象的 ObjectMapper 属性,进行覆盖式设置;
  4. 构造一个新的 MappingJackson2HttpMessageConverter 对象,这样就会为每个请求创建一个全新的实例,而不会出现线程安全性问题;
  5. 将该新对象中的 supportedMediaTypes 与默认的实例保持一致;
  6. 构造一个属于自己的代理对象并将其添加到 HttpMessageConverters 列表中。

这样就完成了对控制器方法的返回值进行全局过滤的操作,下面举两个例子说明如何使用。

例子

例子1:针对控制器对全部接口统一过滤

@RestController
@RequestMapping(value = "/example")
public class ExampleController {

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public ExampleData getById(@PathVariable String id) {
        return new ExampleData(id, "example data");
    }

    ...

    @RequestMapping(value = "/filtered/{id}", method = RequestMethod.GET)
    public ExampleData filteredGetById(@PathVariable String id) {
        return new ExampleData(id, "example data");
    }

}

在 WebMvcConfig 配置文件中的 configureMessageConverters 然后为 ObjectMapper 设置过滤器:

SimpleFilterProvider filterProvider = new SimpleFilterProvider().setFailOnUnknownId(false);
filterProvider.addFilter("exampleDataFilter", new ExampleDataFilter());
objectMapper.setFilterProvider(filterProvider);

通过这样的 global 方法来配置过滤器,可以针对所有的接口起作用(都需要使用该过滤器)。

例子2:针对特定方法过滤

@RestController
@RequestMapping(value = "/example")
public class ExampleController {

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public ExampleData getById(@PathVariable String id) {
        return new ExampleData(id, "example data");
    }

    ...

    @RequestMapping(value = "/filtered/{id}", method = RequestMethod.GET)
    @JsonFilter("exampleDataFilter")//该部分需要加
    public ExampleData filteredGetById(@PathVariable String id) {
        return new ExampleData(id, "example data");
    }

}

这个例子比较特别的地方是特定方法上加了 @JsonFilter("exampleDataFilter"),这就表示针对这个方法,用过滤器 exampleDataFilter 进行过滤。

参考本文的第二步骤,为 ObjectMapper 添加过滤器:

SimpleFilterProvider filterProvider = new SimpleFilterProvider().setFailOnUnknownId(false);
filterProvider.addFilter("exampleDataFilter", new ExampleDataFilter());
objectMapper.setFilterProvider(filterProvider);

对这个方法来说,需要同时在 filterProvider 中注册过滤器。这种情况下,只有针对该方法的返回数据才会受到过滤器的影响,其余接口都不会起作用。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring MVC 更灵活的控制 json 返回问题(自定义过滤字段) - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • java音频播放示例分享(java如何播放音频)

    Java音频播放示例分享 在Java中,我们可以借助Java Sound API来播放音频。本文将详细介绍如何使用Java Sound API来播放音频文件。 首先创建一个播放器类 我们首先需要创建一个播放器类,该类可以使用Java Sound API来播放音频文件。下面是一个基本的播放器类示例: import java.io.File; import ja…

    Java 2023年5月26日
    00
  • Java Date时间类型的操作实现

    下面我将详细的讲解Java Date时间类型的操作实现的完整攻略。 操作Java Date类型 Java中的Date类表示一个特定的时间点,可以用于记录日期时间。下面是一些Java Date操作的方法。 创建Java Date 我们可以使用Date()构造函数来创建Date对象,如下所示: Date date = new Date(); 这将返回当前日期和时…

    Java 2023年5月20日
    00
  • JSP的内部对象

    JSP是Java服务器页面的缩写。它是一种使用Java语言来生成动态Web页面的技术。JSP的内部对象是指在JSP文件中可以访问的预定义的一组Java对象。 JSP的内部对象有以下几个: request对象:代表客户端向服务器发送的HTTP请求。可以用它来获取客户端提交的数据。也可以把需要传递到下一页的数据绑定到它上面,以便在下一页中获取它们。 <!-…

    Java 2023年6月15日
    00
  • Java的Struts框架报错“ActionServletException”的原因与解决办法

    当使用Java的Struts框架时,可能会遇到“ActionServletException”错误。这个错误通常由以下原因之一起: 配置错误:如果配置文件中存在错误,则可能会出现此。在这种情况下,需要检查配置文件以解决此问题。 类加载错误:如果类加载失败,则可能会出现此。在这种情况下,需要检查类路径以解决此问题。 以下是两个实例: 例 1 如果配置文件中存在…

    Java 2023年5月5日
    00
  • 细致解读希尔排序算法与相关的Java代码实现

    细致解读希尔排序算法与相关的Java代码实现 算法介绍 希尔排序(Shell Sort)是插入排序的一种高效的改进算法,也称作缩小增量排序,通过设定一个增量序列来先进行一定量的插入排序,然后逐步减小增量,最后增量为1时再进行一次插入排序,从而达到排序的效果。 希尔排序的过程如下: 设定一个增量序列(如:{1,3,7,15,…}),对于序列进行遍历; 对于…

    Java 2023年5月26日
    00
  • Intellij IDEA 与maven 版本不符 Unable to import maven project See logs for details: No implementation for org.apache.maven.model.path.PathTranslator was bound

    这个错误提示通常是由于Intellij IDEA和Maven版本不匹配导致的。以下是一些解决此问题的攻略: 1. 通过设置maven home目录解决 请先确定你正在使用的Intellij IDEA是否与Maven版本兼容。在Intellij IDEA的Maven设置中,设置正确的Maven home目录。如果Maven home目录没有设置正确,会导致In…

    Java 2023年5月20日
    00
  • Sprint Boot @RefreshScope使用方法详解

    Spring Boot的@RefreshScope注解 在Spring Boot中,@RefreshScope注解用于实现动态刷新配置。通过使用@RefreshScope注解,可以在应用程序运行时动态地刷新配置,而不需要重启应用程序。 @RefreshScope注解的使用方法 以下是@RefreshScope注解的使用方法: 在需要动态刷新的Bean上添加@…

    Java 2023年5月5日
    00
  • Java基础学习之IO流应用案例详解

    Java基础学习之IO流应用案例详解 在Java编程中,输入输出流(IO流)是非常重要的,它是程序中处理文件、网络等数据流的基础。在这里,我们将讲解一些IO流的应用案例,从而更好地理解和掌握Java中的IO流。 一、IO流概念及分类 1.1 IO流简介 IO流指输入/输出流,是Java提供的用于处理数据流的机制。IO流提供了一套函数接口,可方便地进行数据的读…

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部