8、@JsonView 过滤响应正文的对象属性

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相关依赖,无需额外添加。
8、@JsonView 过滤响应正文的对象属性_第1张图片
注意: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接口
8、@JsonView 过滤响应正文的对象属性_第2张图片
调用:/api/example/json-view/detail接口
8、@JsonView 过滤响应正文的对象属性_第3张图片
综上所述:JsonViewInfoDTO.DetailView.class类标识的响应体比JsonViewInfoDTO.ListView.class类标识的响应体多返回了password属性。以此方案实现以相同POJO类作为响应对象时的属性过滤。

三. @JsonView 对象类型的属性字段处理

细心的读者会发现,上面两张截图中,typsattribute属性没能正常返回数据,而是空对象{}。这是因为@JsonView默认只能对基本类型的对象属性进行JSON序列化。而对象类型的对象属性,我们需要通过@JsonSerialize注解指定序列化类

这里作者分别以 JsonViewTypeDTOList 两个对象类型为例,讲解@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,并且以待序列化的POJO类JsonViewTypeDTO作为类型变量。
(2) 覆盖serialize() 方法,实现对JsonViewTypeDTO的序列化。
value:对象实例,包含待序列化对象的所有当前属性值。
gen:序列化后的结果集,我们通过gen.write***方法组装序列化后的JSON数据。
writeStartObject() …… writeEndObject():用于组建json对象,相当于json数据中的 {}
上例中序列化后的json数据,将包含idname两个属性,我们可以根据自己的业务需要构建属性集合。

第二步,在对象属性上使用@JsonSerialize注解,且using值采用JsonViewTypeSerializer.class

……
    /** 对象字段 */
    @JsonView({ListView.class})
    @JsonSerialize(using = JsonViewTypeSerializer.class)
    private JsonViewTypeDTO type;
……
2、List < JsonViewAttributeDTO > 类型属性序列化方案

第一步,我们在serializer包下创建List的序列化类: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的类型变量是我们要序列化的对象类型List,并且在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

你可能感兴趣的:(Java,Web)