Springboot中关于如何设置实体类中的部分属性对用户可见性的方案

前言

关于实体类的属性对用户的可见性问题, 我们第一时间可能会想到定义一个性的类, 里面只定义需要给用户看见的属性, 然后进行属性值拷贝

显然 这种做法极其不优雅, 实体类一旦增多, 后期维护会非常的累

那有没有轻松一点的方法呢, 能不能某个表的实体类就只创建一个, 然后复用呢

答案是 有的

我们可以使用注解来实现, 根据不同的需求, 选择不同的注解, 具体如下:

需求一

用户在请求接口的时候, 实体类中的某些字段不给用户返回(序列化), 同时也不允许用户传值(反序列化), 比如大部分的实体类中我们都会有createTimeupdateTime字段, 像这种是不需要给用户返回的, 同时也不允许用户传值

那么此时我们就可以用到jackson包中的@JsonIgnore注解来实现这一功能, 具体示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Entity
@Table(name = "deleted_user")
data class DeleteUser(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,

@JsonFormat(
pattern = "yyyy-MM-dd HH:mm:ss a",
locale = "zh",
timezone = "GMT+8"
)
@JsonIgnore
val createTime: LocalDateTime = LocalDateTime.now(ZoneId.of("GMT+8")),
)

使用该注解后, 哪怕用户刻意传了也不影响数据, 无论用户传与不传都写入的都是默认值

如果我们需要对多个字段进行可见性隐藏, 除了挨个属性添加@JsonIgnore, 我们还可以在类中使用@JsonIgnoreProperties注解来忽略某个属性, 其效果和@JsonIgnore一样, 具体示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Entity
@Table(name = "deleted_user")
@JsonIgnoreProperties(value = ["createTime"])
data class DeleteUser(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,

@JsonFormat(
pattern = "yyyy-MM-dd HH:mm:ss a",
locale = "zh",
timezone = "GMT+8"
)
val createTime: LocalDateTime = LocalDateTime.now(ZoneId.of("GMT+8")),
)

需求二

用户在请求接口的时候, 实体类中的某些字段需要给用户返回, 但是不允许用户传值, 比如id字段, 那此时我们可以在类中使用@JsonIgnoreProperties注解, 并设置allowGetters = true即可, 具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Entity
@Table(name = "deleted_user")
@JsonIgnoreProperties(value = ["id"],allowGetters = true)
data class DeleteUser(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,

@JsonFormat(
pattern = "yyyy-MM-dd HH:mm:ss a",
locale = "zh",
timezone = "GMT+8"
)
val createTime: LocalDateTime = LocalDateTime.now(ZoneId.of("GMT+8")),
)

需求三

用户在请求接口的时候, 实体类中的某些字段不给用户返回, 但是允许用户传值, 那此时我们可以在类中使用@JsonIgnoreProperties注解, 并设置allowSetters = true即可, 具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Entity
@Table(name = "deleted_user")
@JsonIgnoreProperties(value = ["content"],allowSetters = true)
data class DeleteUser(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
val content: String = "",
@JsonFormat(
pattern = "yyyy-MM-dd HH:mm:ss a",
locale = "zh",
timezone = "GMT+8"
)
val createTime: LocalDateTime = LocalDateTime.now(ZoneId.of("GMT+8")),
)

需求四

用户在请求接口的时候, 实体类中的某些字段不给用户返回, 但是允许用户传值, 同时另外某些字段需要给用户返回, 但是不允许用户传值, 这种又该怎么办呢, 此时我们需要用到另外一个注解@JsonProperty, 在这个注解中配置access值, 具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Entity
@Table(name = "deleted_user")
@JsonIgnoreProperties(value = ["id"],allowGetters = true)
data class DeleteUser(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null,
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
val content: String = "",
@JsonFormat(
pattern = "yyyy-MM-dd HH:mm:ss a",
locale = "zh",
timezone = "GMT+8"
)
val createTime: LocalDateTime = LocalDateTime.now(ZoneId.of("GMT+8")),
)

这样一来, 我们在实现id字段只读的同时, 又实现了content字段的可写入但不可见

access属性常用选项:

  • JsonProperty.Access.WRITE_ONLY: 只允许用户传值, 但不给用户返回

  • JsonProperty.Access.READ_ONLY: 只给用户返回该字段, 但不允许用户传值

需要注意的是: @JsonProperty(access = JsonProperty.Access.READ_ONLY注解放置在父类的属性中无效. 具体原因我也不是太清楚

需求五

假如需要给客户端出两个用户接口, 一个只返回用户名, 另一个返回用户名和用户id, 那这种情况下上面的方案就不适用了, 此时我们需要用到注解@JsonView, 分别作用在属性和Controller中的具体接口方法上, 代码如下:

实体类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity
@Table(name = "user")
@JsonView(Views::class) //这个地方很关键 首先给所有属性一个默认视图
data class User(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@JsonView(Views.Detail::class)//表示id归属于Detail视图
var id: Long? = null,

@JsonView(Views.Basic::class)//表示username归属于Basic视图
val userName: String = "",
)
//用接口来进行视图分类
interface Views {
interface Basic
interface Detail : Basic
}

Controller类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@CrossOrigin
@RestController
@RequestMapping("/user")
class UserController {


@GetMapping(value = ["/userInfoWithId"])
@JsonView(Views.Detail::class)//返回归属于Detail视图的字段
fun getUserInfoWithId(
request: HttpServletRequest
): BaseEntity<EmptyModel> {
val info = tokenUtil.parseToken(request)
val user = service.getUserInfo(info.id, info.userAccount)
return BaseEntity(data = user)

}


@GetMapping(value = ["/userInfo"])
@JsonView(Views.Basic::class)//返回归属于Basic视图的字段
fun getUserInfo(
request: HttpServletRequest
): BaseEntity<EmptyModel> {
val info = tokenUtil.parseToken(request)
val user = service.getUserInfo(info.id, info.userAccount)
return BaseEntity(data = user)

}
}

此时如果你运行程序请求接口发现返回一个空对象{}, 这是由于没有配置objectMapper, 我们需要在WebConfig中进行配置, 具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Configuration
open class WebConfig : WebMvcConfigurer {



@Autowired
private val jsonConverter: MappingJackson2HttpMessageConverter? = null

override fun configureMessageConverters(converters: MutableList<HttpMessageConverter<*>>) {
converters.removeIf { mc -> mc is MappingJackson2HttpMessageConverter }
converters.add(jsonConverter!!)
}

@Bean
open fun objectMapper() : ObjectMapper = ObjectMapper().apply {
setSerializationInclusion(JsonInclude.Include.NON_NULL)
registerModule(KotlinModule())
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
configure(SerializationFeature.WRITE_DATES_WITH_ZONE_ID, true)

}

@Bean
open fun jsonConverter(objectMapper: ObjectMapper): MappingJackson2HttpMessageConverter =
MappingJackson2HttpMessageConverter(objectMapper)




}

上面WebConfig类是我自己创建的, 你如果没有该类则手动创建一个,继承WebMvcConfigurer, 别忘了添加@Configuration注解

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

0%