在 RESTful API 中创建和验证实体模型及其 DTO 的正确方法是啥?

Posted

技术标签:

【中文标题】在 RESTful API 中创建和验证实体模型及其 DTO 的正确方法是啥?【英文标题】:What is the proper way to create and validate an Entity Model and its DTO in a RESTful API?在 RESTful API 中创建和验证实体模型及其 DTO 的正确方法是什么? 【发布时间】:2020-08-08 17:26:57 【问题描述】:

我正在使用 Spring Boot 从头开始​​开发我的第一个 RESTful API。

我已经为“独立”实体创建了端点、模型和 JPA 存储库。但是现在我开始将它们联系在一起,并且在做了一些研究之后,我得出的结论是我可能必须创建 DTO。我不认为每次我使用 POST 请求创建一个新的Order 时,我都应该让客户端将整个CustomerEmployee 对象作为Order 的嵌套对象发送到请求中(如果我也是错了请告诉我)。我正在考虑通过仅用 ID 替换类关系来创建 DTO。

这是我的实体当前的定义方式:

@Data
@Entity
@Table(name = "Orders")
public class Order 

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    @NotBlank
    @NotNull
    private String description;

    @NotBlank
    @NotNull
    private Status status;

    @NotNull
    @ManyToOne
    @JoinColumn(foreignKey = @ForeignKey(name = "employee_id_fk"))
    private Employee employee;

    @NotNull
    @ManyToOne
    @JoinColumn(foreignKey = @ForeignKey(name = "customer_id_fk"))
    private Customer customer;

    protected Order() 

    public Order(String description) 
        this.description = description;
        this.status = Status.IN_PROGRESS;
    

还有我的端点(这是我必须改变的):

    @PostMapping("/orders")
    ResponseEntity<EntityModel<Order>> createOrder(@Valid @RequestBody Order order) 
        order.setStatus(Status.IN_PROGRESS);
        Order newOrder = repository.save(order);

        return ResponseEntity
            .created(linkTo(methodOn(OrderController.class).getOrder(newOrder.getId())).toUri())
            .body(assembler.toModel(newOrder));
    

现在,我应该如何验证这种格式的请求? 以前,如您所见,我会简单地使用@Valid,当针对Order 模型调用端点时,它会自动得到验证。但是,如果我创建 DTO,则必须使用相同的方法验证 DTO,并复制其模型中的所有注释(@NotNull@NotBlank 等)。也许我应该在从 DTO 映射实体模型后对其进行验证,但我不知道这将是多么简单,以及这是否是验证请求的好习惯。我也无法从实体模型中删除验证,因为我使用 Hibernate 将它们映射到表。

【问题讨论】:

【参考方案1】:

好问题!

我不认为每次使用 POST 请求创建新订单时,我都应该让客户端将请求中的整个 Customer 和 Employee 对象作为 Order 的嵌套对象发送(如果我在这方面也错了,请让我知道)。

你是对的。这不是因为我们可以节省位和字节(可能看起来像),而是因为您可以向客户询问的信息越少,他/她获得的体验就越好(无论是外部集成商还是前端/后端-在同一家公司内结束申请)。要包含的数据量更少 = 更容易理解并且出错的空间更小。从设计的角度来看,它还可以使您的 API 更清晰。是否可以在没有该字段的情况下处理您的请求?那么它不应该出现在您的 API 中。

现在,我应该如何验证这种格式的请求?以前,如您所见,我会简单地使用@Valid,它会在针对 Order 模型调用端点时自动得到验证。但是,如果我创建 DTO,则必须使用相同的方法验证 DTO,并复制其模型中的所有注释(@NotNull、@NotBlank 等)。

您还可以使用@Valid 在映射到端点的方法内对控制器内部的 DTO 进行验证。但正如您正确提到的,DTO 中的所有已验证字段都应使用 @NotNull、@NotBlank 等进行注释。作为“重复”问题的解决方案,您可以创建一个基类并在其中定义所有验证并继承 DTO 和来自它的实体。但是请不要那样做!

在 DTO 和 Enity 中具有相同的字段和验证规则不被视为重复,因为它们是独立的概念,并且每个概念都在其层(DTO - 顶层,实体 - 通常最低,数据层)中服务于其特定目的.有很多例子证明了它(例如here和here)

也许我应该在从 DTO 映射实体模型后对其进行验证,但我不知道这将是多么简单,以及这是否是验证请求的好习惯。

验证请求是最佳实践,许多项目都在遵循它。在您的示例中,它非常简单(从 DTO 到实体的直接映射),但通常您会有一个服务层在将其交给数据层之前执行一些业务逻辑(即使在您的示例中,我建议您从控制器到服务层)。您不希望格式错误的请求传递到控制器之外,以便稍后使用过多的 if 语句、空检查(这会导致难以遵循且容易出错的防御性代码)来处理它。

另一个注意事项:您不应该牺牲客户体验并告诉他们或强迫自己添加另外两个字段,因为它允许将一个对象用作 DTO 和实体并简化开发。

最后一点:要将字段从 DTO 映射到实体,您可以使用其中一个对象映射器库。

【讨论】:

以上是关于在 RESTful API 中创建和验证实体模型及其 DTO 的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

了解在“便签本”中创建和更新的 CoreData 实体

如何在 Ember Data 1.13.7 中创建和保存新模型并更新 UI

如何在Java 中创建和验证JWT

在 asp net core MVC 应用程序中创建和验证 JWT 令牌(没有客户端服务器方法)

如何在 Laravel 5.2 中验证 RESTful API?

在猫鼬中创建和查找地理位置