文章目录
SpringMVC过滤(指定返回)对象的某些属性
文章目录
想要了解Jackson相关注解可以直接到第二部分
为什么要过滤对象的某些属性
- 场景:在平时写代码的时候会遇到这样的情况,有实体类,User,这里面定义了User相关的所有信息,同时需要对外开放接口查询获得用户的信息,但是要根据信息的不同,写查询不同的信息的接口,分情况返回数据
- 我需要用户的基本信息,用户名,等级,头像,个人介绍之类的数据,那么不能将密码,余额之类的返回给前端,那么就需要在返回数据的时候过滤掉不需要返回的属性
- 使用工具:SpringMVC以及SpringMVC默认的Json解析,Jackson即可
方法以及代码如下
实体类代码
@Data
public class User {
private String username;
private String password;
private Date brithday;
private String headImage;
private Integer account;
private String address;
private String mobile;
}
Controller代码
@RestController
@RequestMapping("/user/")
public class UserController {
/**
* 获取用户基础信息
* @param username
* @return
*/
@GetMapping("base")
public User getUserBaseInfo(String username){
return User.builder().username("sa").account(213).headImage("http://sss.ss.com").
address("石家庄").password("123456").mobile("13776684587").build();
}
/**
* 获取用户收货地址信息
* @param username
* @return
*/
@GetMapping("address")
public User getUserAddressInfo(String username){
return User.builder().username("sa").account(213).headImage("http://sss.ss.com").
address("石家庄").password("123456").mobile("13776684587").build();
}
/**
* 获取用户密码信息
* @param username
* @return
*/
@GetMapping("pass")
public User getUserLoginInfo(String username){
return User.builder().username("sa").account(213).headImage("http://sss.ss.com").
address("石家庄").password("123456").mobile("13776684587").build();
}
}
相关方法以及注解,以及优劣
使用Jackson注解:@JosnIgnore
- 该注解一般使用在成员变量上,表示,在Jackson序列化的时候,忽略该属性。
- 使用情况:一些不需要返回给前端的属性可以使用该注解忽略掉
@Data
@Builder
public class User {
private String username;
@JsonIgnore
private String password;
private Date brithday;
private String headImage;
@JsonIgnore
private Integer account;
@JsonIgnore
private String address;
@JsonIgnore
private String mobile;
}
- 将不需要返回的属性在实体类标注出来,即可,弊端:忽略效果,全局起效,每个接口都会忽略掉该属性
使用注解@JsonView
- 该注解用于指定,序列化的视图规则,用于两个地方,一个是实体类的属性上,另外一个是controller的接口方法上
- 使用:
2.1 首先需要先建立视图(可以在实体类内部创立,也可以单独创建视图类)
private interface BaseView{}
@JsonView(User.BaseView.class)
public String username;
private String password;
@JsonView(User.BaseView.class)
private Date brithday;
@JsonView(User.BaseView.class)
private String headImage;
private Integer account;
private String address;
private String mobile;
2.2 在需要返回的属性上面加上JsonView注解并表明,属于那个视图
2.3 在controller接口中指定视图名称即可
/**
* 获取用户基础信息
* @param username
* @return
*/
@GetMapping("base")
@JsonView(User.BaseView.class)
public User getUserBaseInfo(String username){
return User.builder().username("sa").account(213).headImage("http://sss.ss.com").
address("石家庄").password("123456").mobile("13776684587").build();
}
2.3 这样在访问用户基础信息接口的时候就只会把有@JsonView(User.BaseView.class)注解的属性序列化返回给前端,在访问没有加@JsonView注解的controller时依然还是返回user的全部属性。
3. 这样使用的话,对于一个接口没问题,那其他接口,也想有这个效果呢,那我怎么整?一样的,可以针对另外一个接口也创建一个视图,然后将需要返回的属性上面加上注解,然后对应视图名称,controller上指定对应的视图即可
@Data
@Builder
public class User {
public interface BaseView{}
public interface AddressView{}
@JsonView({User.BaseView.class,User.AddressView.class})
private String username;
private String password;
@JsonView(User.BaseView.class)
private Date brithday;
@JsonView(User.BaseView.class)
private String headImage;
private Integer account;
@JsonView(User.AddressView.class)
private String address;
@JsonView(User.AddressView.class)
private String mobile;
}
/**
* 获取用户基础信息
* @param username
* @return
*/
@GetMapping("base")
@JsonView(User.BaseView.class)
public User getUserBaseInfo(String username){
return User.builder().username("sa").account(213).headImage("http://sss.ss.com").
address("石家庄").password("123456").mobile("13776684587").build();
}
/**
* 获取用户收货地址信息
* @param username
* @return
*/
@GetMapping("address")
@JsonView(User.AddressView.class)
public User getUserAddressInfo(String username){
return User.builder().username("sa").account(213).headImage("http://sss.ss.com").
address("石家庄").password("123456").mobile("13776684587").build();
}
- 这样其实很好理解,就相当于我要把实体类中的属性都管理起来,想返回什么,就返回什么,不能多也不能少,这个统一管理的就是JsonView注解,在实体类上加上这个注解,并表明注解对应视图名称,那么就将这个属性纳管到该注解的指定视图中了,在controller时就可以规定返回某个视图纳管下的所有属性,不是这个视图管辖范围内的,不序列化,不返回。
进阶使用(一)
- 如果我定义的视图太多了,有些属性是无论那个接口都要返回的,例如username,那么这个属性上面岂不是要加所有的视图名称?
- 可以在属性上面加上若干个视图的名称,但如果是公用的,所有接口都返回的,那么就可以先定义一个基础的视图,让其他视图继承这个视图,在公用的属性上面加上基础视图名称即可,因为JsonView(XXX.class)在序列化的时候,不仅能纳管到XXX.class的属性,也可以纳管到XXX父类的属性,代码如下:
@Data
@Builder
public class User {
public interface View{}
public interface BaseView extends View{}
public interface AddressView extends View{}
public interface PassView extends View{}
@JsonView({User.View.class})
private String username;
@JsonView(User.PassView.class)
private String password;
@JsonView(User.BaseView.class)
private Date brithday;
@JsonView(User.BaseView.class)
private String headImage;
private Integer account;
@JsonView(User.AddressView.class)
private String address;
@JsonView(User.AddressView.class)
private String mobile;
}
//Controller 代码不变
进阶使用(二)
- 一般在生产环境中,我们不可能直接在接口中返回一个User对象给前端,我们都会包装一层然后返回。
@Data
@Builder
public class ResponseEntity<T> {
private String message;
private int status;
private T t;
public static <T> ResponseEntity<T> success(T t){
return (ResponseEntity<T>) ResponseEntity.builder().message("成功!").status(200).t(t).build();
}
}
Controller代码如下
/**
* 获取用户密码信息
* @param username
* @return
*/
@GetMapping("pass")
public ResponseEntity getUserLoginInfo(String username){
User build = User.builder().username("sa").account(213).headImage("http://sss.ss.com").
address("石家庄").password("123456").mobile("13776684587").build();
return ResponseEntity.success(build);
}
- 此时如果直接在controller接口上加上@JsonView(User.PassView.class),你会发现啥也没返回,为什么呢,因为SpringMVC序列化的时候,如果你指定了视图,那么他会以你指定的视图为优先级,去ResponseEntity中 去查看,是否有加了这个视图的属性,发现没有,那么什么都不返回。
- 解决方法,就是要在ResponseEntity中加上@JsonView(User.PassView.class)
@JsonView(User.PassView.class)
private String message;
@JsonView(User.PassView.class)
private int status;
//只在此加的话,只会返回t的属性值,message,以及status依然不会返回
@JsonView(User.PassView.class)
private T t;
- 新问题,我其他的接口也需要返回ResponseEntity怎么办,那就是继续加,并且把最基础的加上最基础的视图名称,所以最终的代码为:
User:
@Data
@Builder
public class User {
public interface View{}
public interface BaseView extends View{}
public interface AddressView extends View{}
public interface PassView extends View{}
@JsonView({User.View.class})
private String username;
@JsonView(User.PassView.class)
private String password;
@JsonView(User.BaseView.class)
private Date brithday;
@JsonView(User.BaseView.class)
private String headImage;
private Integer account;
@JsonView(User.AddressView.class)
private String address;
@JsonView(User.AddressView.class)
private String mobile;
}
ResponseEntity:
@Data
@Builder
public class ResponseEntity<T> {
@JsonView(User.View.class)
private String message;
@JsonView(User.View.class)
private int status;
@JsonView({User.PassView.class,User.AddressView.class,User.BaseView.class})
private T t;
public static <T> ResponseEntity<T> success(T t){
return (ResponseEntity<T>) ResponseEntity.builder().message("成功!").status(200).t(t).build();
}
}
Controller代码:
/**
* 获取用户基础信息
* @param username
* @return
*/
@GetMapping("base")
@JsonView(User.BaseView.class)
public ResponseEntity getUserBaseInfo(String username){
User build = User.builder().username("sa").account(213).headImage("http://sss.ss.com").
address("石家庄").password("123456").mobile("13776684587").build();
return ResponseEntity.success(build);
}
/**
* 获取用户收货地址信息
* @param username
* @return
*/
@GetMapping("address")
@JsonView(User.AddressView.class)
public ResponseEntity getUserAddressInfo(String username){
User build = User.builder().username("sa").account(213).headImage("http://sss.ss.com").
address("石家庄").password("123456").mobile("13776684587").build();
return ResponseEntity.success(build);
}
/**
* 获取用户密码信息
* @param username
* @return
*/
@GetMapping("pass")
@JsonView(User.PassView.class)
public ResponseEntity getUserLoginInfo(String username){
User build = User.builder().username("sa").account(213).headImage("http://sss.ss.com").
address("石家庄").password("123456").mobile("13776684587").build();
return ResponseEntity.success(build);
}
写到最后,还有其他的用法,读者可以自己下去深入研究,我分享的生产环境使用足够,除非需求比较变态。
Jackson相关注解使用
使用Jackson注解:@JosnIgnore
1. 该注解一般使用在成员变量上,表示,在Jackson序列化的时候,忽略该属性
2. 使用情况:一些不需要返回给前端的属性可以使用该注解忽略掉
3. 弊端:忽略效果,全局起效,每个接口都会忽略掉该属性,但是有时候我们有很多接口,一些属性A接口不需要返回,但是B接口要将该属性返回,此时全部忽略就不适用了
@JsonIgnoreProperties:@JsonIgnore的进阶版
1. 作用在类上,指定要忽略的属性
2. @JsonIgnoreProperties(value = {"name","password"})
@JsonProperty
1. 该注解用在属性上,作用:在jackson序列化的时候,将属性名称改成注解内的属性
2. 使用情况:返回前端的数据,与实体类的属性名不一样,@JsonProperty("name")
@JsonProperty("name")
private String username;
3. 弊端:全局效果,并且该注解在反序列化的时候也起效,springmvc接收数据也起效
@JsonInclude
1. 该注解作用在类或者属性上,控制什么情况下属性才能(或不能)被序列化
2. 使用情况: @JsonInclude(JsonInclude.Include.NON_NULL) 值不为空的属性,空的属性不序列化
3. 弊端:只生效于序列化
@JsonPropertyOrder
1. 该注解作用在类,或者属性上,标识属性序列化的顺序
2. 使用:一般作用在类上, @JsonPropertyOrder({"name","password","account"}),指定属性序列化顺序
3. 弊端:只生效于序列化
@JsonSetter
1. 该注解作用在属性,和setter方法上,在反序列化时,按照注解的配置注入到加注解的属性
2. 使用:@JsonSetter("name"),类似于JsonProperty
@JsonSetter("name")
private String username;
3. 弊端:仅适用于反序列化的情况
@JsonFormat
1. 该注解一般用于,服务端将Date类型的属性返回给前端的时候,格式化Date的形式
2. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
3. 仅仅用于序列化的时候,需要反序列化时间格式需要使用 @DateTimeFormat(Spring的注解,用于反序列化时间格式, @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss"))
@JsonView
1. 该注解用于指定,序列化的视图规则,用于两个地方,一个是实体类的属性上,另外一个是controller的接口方法上
2. 实体类上:指定这个属性只存在于注解标识的视图类中,controller方法上:指定返回该对象时使用指定的视图类
有问题可以私信我,看到会回答