当前位置: 首页 > 知识库问答 >
问题:

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

淳于飞鸾
2023-03-14

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

我已经为“独立”实体创建了endpoint、模型和JPA存储库。但现在我开始将它们联系在一起,在做了一些研究之后,我得出结论,我可能必须创建DTO。我不认为每次我通过POST请求创建新的订单,我都应该让客户将整个客户员工对象作为订单的嵌套对象发送到请求中(如果我在这方面也有错误,请告诉我)。我正在考虑创建一个DTO,只需将类关系替换为ID即可。

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

@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;
    }
}

以及我的endpoint(这是我必须更改的):

    @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,当针对订单模型调用endpoint时,它会自动得到验证。但是,如果我创建DTO,我必须使用相同的方法验证DTO,并从其模型复制所有注释(@NotNull@NotBlank,等等)。也许我应该在从DTO映射实体模型之后对其进行验证,但我不知道这有多简单,也不知道这是否是验证请求的良好实践。我也无法从实体模型中删除验证,因为我正在使用Hibernate将它们映射到表。

共有1个答案

拓拔意
2023-03-14

伟大的问题!

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

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

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

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

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

也许我应该在从DTO映射实体模型之后对其进行验证,但我不知道这有多简单,也不知道这是否是验证请求的良好实践。

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

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

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

 类似资料:
  • 我发布了简单的数据,比如 到(让它成为)/帖子乌里,例如smth喜欢 我可以验证,该正文不是空的 或者检查,响应的字段具有我们正在设置的值,例如 但我不确定,它的最佳/正确的方式。那么,问题:如何验证,该实体是在发布后创建的,并保证?

  • 我想知道在静态编程语言中创建全局常量的最佳方法是什么。Java,我们将使用一个包含常量的类,我们只需要导入这个类就可以访问所有常量。但是在静态编程语言中,有两种主要的方法: > 您可以创建一个包含所有常量的对象: 对象常量{const valCONST_1="foo"const valCONST_2="bar"const valCONST_3="toto"} 但这不是推荐的方式,正如一位语言开发者

  • 我是一个项目的一部分,我正在尝试添加TypeScript Web Server,这将与Swagger兼容。 什么是最基本的策略来实现它,考虑到容易的可运维性。 > 对于TypeScript,我注意到存在用于从TypeScript接口生成JSON模型的Tyson库。 对于Swagger,我尝试使用“Swagger node restify”库,因为它支持向Swagger添加JSON模型。 然而,我遇

  • 问题内容: 我有一个简单的班级角色: 现在,我想重写其方法equals和hashCode。我的第一个建议是: 但是,当我创建新的Role对象时,其id为null。这就是为什么我对hashCode方法实现有一些问题。现在,我可以简单地返回,但是如果没有RoleName字段,该怎么办?我几乎可以肯定,组成更复杂的示例并不难,而通过返回其字段之一的hashCode不能解决该示例。 因此,我想查看一些指向

  • 我正在使用Swing和Java。我刚刚开始学习MVC。 我对验证表单(视图)所需的方式有点困惑。 我的想法是在我的视图类中创建方法并逐个验证表单组件,但我担心这会违反MVC原则。

  • 如果我有实体类 和一个DTO > 我的问题是我应该在客户端中使用(赢形)吗?或创建另一个类 如果我想要一个方法 我应该把它放在哪里(作为静态的辅助方法,还是放在里面)?我以前认为应该是里面的方法,但是贫血模型不允许有任何方法。