HTTP 教程:https://www.w3cschool.cn/http/a96bxfml.html
HTTP请求返回接口类型控制(json/xml):https://www.cnblogs.com/dayou123123/p/8298917.html
HttpMessageConverter:https://www.jianshu.com/p/3e1de3d02dd8
本章演示代码:https://gitee.com/tysite-web/tysite-service/tree/master/src/main/java/org/tysite/tyservice/example/jsonview
说明
在前后端分离的项目中,服务端接口不需要关注WEB页面的渲染工作,只需要专注于业务数据的回传和状态信息反馈,然后由前端同学完成页面渲染。所以在日常开发中,我们会通过HTTP状态码标记响应状态,通过响应正文传递接口回传信息。
为了更好的格式化响应信息,当前主流的方式是采用JSON格式返回。spring mvc 为我们提供了@ResponseBody注解,可以根据请求头的Accept参数值,调用对应的响应转换器HttpMessageConverter将请求结果对象以对应格式写入响应体(jackson默认情况下不支持xml格式,会以json格式返回)
在日常开发的工作中,通常遇到不同业务场景下,相同结果对象所需要返回的字段信息不同。
例如:
1、面对用户信息接口,管理端使用时,需要返回用户的手机号、身份证号码等敏感信息,以便管理员查看、修改;而用户端使用时则仅需要呈现姓名、头像等非敏感信息。
2、管理列表呈现的字段信息与数据编辑界面呈现的数据也有不同。
Spring MVC 为我们提供了@JsonView注解,实现对结果对象的回显字段过滤功能,本文将详细讲解@JsonView的使用及推荐用法。
一. 依赖添加
在我们的tysite-service项目搭建时,依赖使用的是org.springframework.boot:spring-boot-starter-web,该依赖集中已经包含com.fasterxml.jackson.core:jackson-databind相关依赖,无需额外添加。

注意:tysite-service项目以返回json格式数据为目标,故不能添加com.fasterxml.jackson.dataformat:jackson-dataformat-xml依赖,否则chrome浏览器默认会以xml格式返回。
二. @JsonView 属性过滤
@JsonView 通过类标识标注不同的属性过滤方案,并在Controller方法上使用对应过滤方案的类标识,得到预期的请求体JSON数据。
注意: 上面说的类标识可以是class,也可以是interface。作者建议采用POJO的内部接口来定义过滤方案的类标识。
具体实现方案如下:
第一步,在作为结果集返回对象的POJO类中,根据业务需求定义 内部接口 作为类标识。
……
public interface ListView {}
public interface DetailView extends ListView{}
……
注意: 类标识支持继承关系,如果属性注释了子类,则相当于其父类也生效。
第二步,根据业务需求,通过@JsonView注解注释相关属性
……
/** 数字字段 */
@JsonView({ListView.class})
private Integer id;
/** 名称字段 */
@JsonView({ListView.class})
private String name;
/** 密码字段 */
@JsonView({DetailView.class})
private String password;
……
演示对象源码地址:JsonViewInfoDTO
第三步,完成Controller接口注释
……
@GetMapping("/list")
@JsonView(JsonViewInfoDTO.ListView.class)
public JsonViewInfoDTO demoList() {
return jsonViewService.getJsonViewInfo();
}
@GetMapping("/detail")
@JsonView(JsonViewInfoDTO.DetailView.class)
public JsonViewInfoDTO demoDetail() {
return jsonViewService.getJsonViewInfo();
}
……
演示Controller源码地址:JsonViewController
第四步,启动项目,分别调用列表和详情接口,查看响应体的属性过滤效果。
调用:/api/example/json-view/list接口

调用:/api/example/json-view/detail接口

综上所述:JsonViewInfoDTO.DetailView.class类标识的响应体比JsonViewInfoDTO.ListView.class类标识的响应体多返回了password属性。以此方案实现以相同POJO类作为响应对象时的属性过滤。
三. @JsonView 对象类型的属性字段处理
细心的读者会发现,上面两张截图中,typs和attribute属性没能正常返回数据,而是空对象{}。这是因为@JsonView默认只能对基本类型的对象属性进行JSON序列化。而对象类型的对象属性,我们需要通过@JsonSerialize注解指定序列化类。
这里作者分别以 JsonViewTypeDTO 和 List<JsonViewAttributeDTO> 两个对象类型为例,讲解@JsonSerialize的用法。
1、JsonViewTypeDTO 类型属性序列化方案
第一步,我们在JsonViewTypeDTO.java类所在的dto包下,创建 serializer 包,并在该包中,创建JsonViewTypeDTO的Json序列化处理类:JsonViewTypeSerializer
public class JsonViewTypeSerializer extends JsonSerializer<JsonViewTypeDTO> { (1)
@Override
public void serialize(JsonViewTypeDTO value, JsonGenerator gen, SerializerProvider serializers) throws IOException { (2)
gen.writeStartObject();
gen.writeNumberField("id", value.getId());
gen.writeStringField("name", value.getName());
gen.writeEndObject();
}
}
(1) 序列化类JsonViewTypeSerializer继承泛型类JsonSerializer<T>,并且以待序列化的POJO类JsonViewTypeDTO作为类型变量。
(2) 覆盖serialize() 方法,实现对JsonViewTypeDTO的序列化。
value:对象实例,包含待序列化对象的所有当前属性值。
gen:序列化后的结果集,我们通过gen.write***方法组装序列化后的JSON数据。
writeStartObject() …… writeEndObject():用于组建json对象,相当于json数据中的 {}。
上例中序列化后的json数据,将包含id和name两个属性,我们可以根据自己的业务需要构建属性集合。
第二步,在对象属性上使用@JsonSerialize注解,且using值采用JsonViewTypeSerializer.class。
……
/** 对象字段 */
@JsonView({ListView.class})
@JsonSerialize(using = JsonViewTypeSerializer.class)
private JsonViewTypeDTO type;
……
2、List < JsonViewAttributeDTO > 类型属性序列化方案
第一步,我们在serializer包下创建List<JsonViewAttributeDTO>的序列化类:JsonViewAttributeListSerializer
public class JsonViewAttributeListSerializer extends JsonSerializer<List<JsonViewAttributeDTO>> {
@Override
public void serialize(List<JsonViewAttributeDTO> value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (null != value && value.size() > 0) {
gen.writeStartArray();
for (JsonViewAttributeDTO attributeDTO : value) {
gen.writeStartObject();
gen.writeNumberField("id", attributeDTO.getId());
gen.writeStringField("name", attributeDTO.getName());
gen.writeEndObject();
}
gen.writeEndArray();
}
}
}
从上面的示例代码中,我们可以发现JsonSerializer<T>的类型变量是我们要序列化的对象类型List<JsonViewAttributeDTO>,并且在serialize()方法中,我们先判断List值是否为空,当非空状态下,对属性对象进行循环处理。
注意:writeStartArray() / writeEndArray()相当于json数据中的 [],writeStartObject() / writeEndObject()相当于json数据中的 {}
第二步,在对象属性上使用@JsonSerialize注解,且using值采用JsonViewAttributeListSerializer.class。
……
/** 对象列表字段 */
@JsonView({ListView.class})
@JsonSerialize(using = JsonViewAttributeListSerializer.class)
private List<JsonViewAttributeDTO> attribute;
……
四. 作者推荐
在@JsonView的使用中,作者给出如下建议:
1、为了便于管理类标识,所有类标识均采用需要使用@JsonView过滤属性的POJO类(entity或dto)的内部接口实现。
2、类标识视图支持继承,建议所有存在字段包含关系的内部接口采用继承方案。
3、@JsonView({A.class, B.class})支持数组,可将多个必要且不包含继承关系的类标识视图放在同一个@JsonView注解中。
4、强烈建议,所有Controller接口,结果对象必须进行@JsonView过滤。
5、POJO类的序列化处理类,建议存放在该类所在包的serializer包下,便于后期维护。
|- controller
|- entity // 存放持久层对象
|- serializer //序列化处理类(无序列化处理类可不创建该包)
|- dto // 存放数据传输对象
|- serializer //序列化处理类(无序列化处理类可不创建该包)
|- service
本文详细介绍如何使用SpringMVC的@JsonView注解对响应数据进行字段过滤,以适应不同业务场景的需求。同时,介绍了如何利用@JsonSerialize注解处理复杂对象类型的序列化问题。

1877

被折叠的 条评论
为什么被折叠?



