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");
}
}
该类中只有两个重写的方法比较重要:serializeAsField
和 include
。
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
,则针对该转换器做以下修改:
- 获取该转换器的 ObjectMapper 对象,默认设置为排除 null 的空对象;
- 为该 ObjectMapper 对象添加新的过滤器;
- 重新配置
MappingJackson2HttpMessageConverter
对象的 ObjectMapper 属性,进行覆盖式设置; - 构造一个新的
MappingJackson2HttpMessageConverter
对象,这样就会为每个请求创建一个全新的实例,而不会出现线程安全性问题; - 将该新对象中的
supportedMediaTypes
与默认的实例保持一致; - 构造一个属于自己的代理对象并将其添加到 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技术站