从返回域对象列表的 RESTful Web 服务端点的响应中动态过滤字段
Posted
技术标签:
【中文标题】从返回域对象列表的 RESTful Web 服务端点的响应中动态过滤字段【英文标题】:Dynamic filtering of a field in the response from a RESTful webservice enpoint that returns a List of domain objects 【发布时间】:2020-07-12 08:11:22 【问题描述】:鉴于使用 Spring Boot 框架开发的 RESTful Web 服务,我想要一种方法来抑制响应中所有用户的 birthDate
。这是我在寻找解决方案后实施的:
@RestController
public class UserResource
@Autowired
private UserDAOservice userDAOService;
@GetMapping("/users")
public MappingJacksonValue users()
List<User> users = userDAOService.findAll();
SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter
.filterOutAllExcept("id", "name");
FilterProvider filters = new SimpleFilterProvider().addFilter(
"UserBirthDateFilter", filter);
MappingJacksonValue mapping = new MappingJacksonValue(users);
mapping.setFilters(filters);
return mapping;
但是,当我在浏览器中点击休息端点时,我仍然可以在响应中看到用户的出生日期:
"id": 1,
"name": "Adam",
"birthDate": "1980-03-31T16:56:28.926+0000"
问题 1:我可以使用什么 API 来实现我的目标?
接下来,假设我想坚持HATEOAS 结合过滤,我该怎么做。我无法弄清楚可用于同时使用这两个功能的 API:
@GetMapping("/users/id")
public EntityModel<User> users(@PathVariable Integer id)
User user = userDAOService.findById(id);
if (user == null)
throw new ResourceNotFoundException("id-" + id);
EntityModel<User> model = new EntityModel<>(user);
WebMvcLinkBuilder linkTo = linkTo(methodOn(this.getClass()).users());
model.add(linkTo.withRel("all-users"));
//how do I combine EntityModel with filtering?
return model;
问题 2:如何将EntityModel
与MappingJacksonValue
结合起来?
注意:我知道@JsonIgnore
注释,但这将对使用域的所有端点应用过滤器;但是,我想将过滤限制在上面的两个端点。
【问题讨论】:
【参考方案1】:事实证明,我必须在 DTO 上方添加 @JsonFilter
注释,并提供创建 SimpleFilterProvider
时使用的相同名称。
@JsonFilter("UserBirthDateFilter")
public class User
private Integer id;
@Size(min=2, message="user name must be atleast 2 characters")
@ApiModelProperty(notes="user name must be atleast 2 characters")
private String name;
@Past
@ApiModelProperty(notes="birth date cannot be in the past")
private Date birthDate;
//other methods
【讨论】:
【参考方案2】:有一种更简单的方法可以做到这一点,在您的传输对象(您发送回客户端的类)上,您可以简单地使用 @JsonIgnore 注释来确保该字段未序列化,因此发送到客户。因此,只需在您的 User 类中为您的birthDay 字段添加@JsonIgnore。
您还可以在此处阅读有关此方法的更多信息:
https://www.baeldung.com/jackson-ignore-properties-on-serialization
如果您需要为不同的端点返回不同的对象(在您的情况下,用户没有birthDay,仅用于特定),您应该创建单独的传输对象并将它们用于各自的端点。您可以将构造函数中的原始实体(用户)传递给这些类并复制所有需要的字段。
【讨论】:
这将是适用于所有端点的“静态过滤”。我正在寻找仅适用于问题中显示的两个端点的选择性过滤。对于所有其他端点,我想在响应中显示birthdDate
。
在这种情况下,我认为让您最开心的是创建一个单独的 UserWithoutBirthDay DTO 类或类似的类,它在其构造函数中采用一个 User 对象。不幸的是,Java 在这方面相当薄弱【参考方案3】:
您可以使用 Jackson 的 @JsonView 功能。有了这个,您可以告诉某个请求映射以生成具有所选属性集的序列化 JSON。
public class View
interface UserDetails
public class User
@JsonView(View.UserDetails.class)
private Long id;
@JsonView(View.UserDetails.class)
private String name;
private String birthdate;
控制器就像
@JsonView(View.UserDetails.class)
@GetMapping("/users")
public MappingJacksonValue users()
....
【讨论】:
我的目标是使用 SimpleBeanPropertyFilter API 实现这一目标。它似乎不适用于此 API。 但是你没有提到任何地方,为什么强制使用 SimpleBeanPropertyFilter ?【参考方案4】:对于第 2 个问题,我的问题与您的问题完全相同,这就是我所做的。它似乎正在工作:
@GetMapping(path = "/users/id")
public MappingJacksonValue retrieveUser(@PathVariable int id)
User user = service.findOne(id);
if(user==null)
throw new UserNotFoundException("id-"+id);
//"all-users", SERVER_PATH + "/users"
EntityModel<User> resource = EntityModel.of(user);
WebMvcLinkBuilder linkTo =
linkTo(methodOn(this.getClass()).retrieveAllUsers());
resource.add(linkTo.withRel("all-users"));
SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept("id");
FilterProvider filters = new SimpleFilterProvider().addFilter("UserFilter",filter);
MappingJacksonValue mapping = new MappingJacksonValue(resource);
mapping.setFilters(filters);
return mapping;
响应 HTTP GET localhost:8080/users/1
"id": 1,
"links": [
"rel": "all-users",
"href": "http://localhost:8080/users"
]
【讨论】:
以上是关于从返回域对象列表的 RESTful Web 服务端点的响应中动态过滤字段的主要内容,如果未能解决你的问题,请参考以下文章
如何在我的 Rails RESTful Web 服务中接受 PUT 和 DELETE 方法