从返回域对象列表的 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:如何将EntityModelMappingJacksonValue 结合起来?

注意:我知道@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 服务端点的响应中动态过滤字段的主要内容,如果未能解决你的问题,请参考以下文章

Web开发中怎么用RESTful做后端API

Web开发中怎么用RESTful做后端API

如何在我的 Rails RESTful Web 服务中接受 PUT 和 DELETE 方法

返回相同对象列表的 RESTful 方式

Web Service简介(RESTful Web Services 第一章笔记)

返回 JSONP 的 Restful api 的节点 HTTP 请求