【问题标题】:Jersey: Best practice handling restful HTTP PUT (update) request泽西岛:处理 RESTful HTTP PUT(更新)请求的最佳实践
【发布时间】:2016-01-09 10:01:46
【问题描述】:

假设我有非常简单的User 类:

@JsonIgnoreProperties({"password", "created", "lastModified"})
public class User {
    public String id;
    public String name;
    public String password
    public Long created;
    public Long lastModified;
}

当我们将 User 实体序列化为 JSON 字符串时,我们会过滤掉 passwordcreatedlastModified。然后前端的用户会更新用户名并将用户放回我们的Restful服务。

我的伪控制器方法处理PUT 请求:

@Path("/user/{id}")
@POST
@Consumes(MediaType.APPLICATION_JSON)
public void saveUpdate(@PathParam("id")String id, User user) {
    userService.save(user);
}

在上述方法中,我希望Jackson将前端发送的JSON字符串反序列化到POJOuser实例,然后调用我们的服务保存实例。

到目前为止,一切看起来都很酷。然而,当我们将user POJO 传递给userService 以保存它时,引发了担忧。请记住,当我们将user 序列化为 JSON 时,我们忽略了三个属性:passwordcreatedlastModified,我们必须从数据库中获取这些缺失属性的值,然后与从发送的数据合并前端。

进行合并操作对于 coder 来说是一项非常简单但繁琐的任务。我想知道是否有任何好的做法可以轻松优雅地处理这个案例

【问题讨论】:

    标签: java rest jersey put pojo


    【解决方案1】:

    我想 User 对象是要保存到 DB 中的实体对象,因此最佳做法是不要将实体对象公开给 FE,而是使用仅包含 FE 需要知道的字段的 DTO 对象(考虑隐藏 id 和使用 UUID 代替,因为 id 值是可预测的)。稍后您可以使用ModelMapper 将此类 DTO 对象映射到 Entity,这样您就可以轻松地在 userDTO 和用户实体对象之间合并,然后只保留实体而无需担心缺少字段的状态

    此外,现在许多 Web 服务禁用其应用程序的 PUT 请求并改用 POST。我不太了解原因,但这是我们将应用程序传递给安全检查时的安全问题之一

    【讨论】:

    • by id 我的意思是使用的通用 ID,可以是 UUID,或者下划线系统中使用的任何其他 ID; DTO 不是我喜欢的东西。代码越少越好;您能否指出一个使用ObjectMapper 示例的链接? PUT 不被鼓励,因为许多防火墙阻止了它。您仍然可以使用PUT,但通过POST 伪造它,并将X-HTTP-Method-Override 标头设置为PUT
    • 实际上PUT 在这种情况下是不合适的,因为从语义上讲,它是假设更新User 实体的特定属性,而不是整个实体。我在问题中将其更改为POST
    • 我只是快速谷歌ObjectMapper 并发现它不能帮助您将DTO 映射到实体。事实上,它将您的 Java 对象转换为 JSON 字符串/从 JSON 字符串转换。
    • 感谢 Evgeny。我很欣赏ModelMapper,我给你一个,看起来不错。但是我不使用 DTO 方法,它重复了太多代码
    • 你可以在不想暴露给客户的字段上使用带有@JsonIgnore注解的实体类
    【解决方案2】:

    我认为问题更多在于您的服务而不是 REST API。我的意思是,如果用户的 3 个属性“password”、“created”和“lastModified”对表示层不可见,那么服务应该能够在没有它们的情况下处理保存。

    在持久层中,您绝对可以在不填充所有属性的情况下更新User。或者更好的设计,让我们将指向同一个数据库表的 UserLogin("id", "password", "created", "lastModified") 和 User("id", "name") 分开。然后对 User 的更新根本不需要其他不必要的属性。

    【讨论】:

    • UserLogin 方法看起来不错。然而,这些东西是“创建的”、“lastModified”,也许其他一些字段在所有模型类中都是通用的,这意味着我需要为所有模型类定义一个单独的 DTO 类来应用该方法。这看起来不太干......
    • 明白你的意思。这样的审计信息,如createdlastModified,在所有域对象之间都是通用的。所以,通常,我会为这些属性创建一个基础对象,例如AuditableEntity(created, lastModified, createdUser, lastModifiedUser)。这些基本属性将由一个中心点处理,而不是在所有服务中复制,例如 Hibernate listener docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/…。我认为我们达到了 S(单一责任)和 D(干)。
    • 我已经有了处理createdlastModified 的通用方法。问题是如果我像上面提到的UserLogin 一样使用DTO,我可以轻松地将更新数据合并到模型实体中,但我需要为每个模型类创建一个单独的DTO 类,这不是DRY。如果我不创建DTO,我将面临在保存到数据库之前将null 等公共属性(如createdlastModified)的值合并到实体中的问题。 lastModified 可能会更容易,因为无论如何我们都会更新它,但对于像 created 这样的其他事情,并不是那么简单
    猜你喜欢
    • 2014-10-06
    • 2011-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-30
    • 1970-01-01
    • 2011-08-18
    • 2012-06-04
    相关资源
    最近更新 更多