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

标签 java spring spring-boot rest api

我正在使用 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 将它们映射到表。

最佳答案

很好的问题!

I don't think everytime I'm creating a new Order with a POST request I should make the client send the whole Customer and Employee objects inside the request as nested objects of Order (if I am also wrong in this please let me know).

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

Now, how should I validate the requests with this format? Previously, as you can see, I would simply use @Valid and it would automatically get validated when the endpoint is called against the Order model. However, if I create the DTO, I would have to validate the DTO with the same methodology and duplicate all the annotations from its model (@NotNull, @NotBlank, etc.).

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

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

Maybe I should validate the entity model after mapping it from the DTO but I don't know how straightforward that would be and whether that is a good practice of validating requests.

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

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

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

关于java - 在 RESTful API 中创建和验证实体模型及其 DTO 的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61420237/

相关文章:

java - 如何使用 setProperty 设置不同的信任库 keystore

spring - FileSplitter 完成后重命名文件

java - 关于如何将我的 spring boot web 应用程序部署到 tomcat8 的问题

java - 在调试期间重新组织列表顺序

java - Java HashMap.clear() 和 remove() 内存有效吗?

java - 使用外部程序从 Assets 中打开图像

mysql - Spring Boot JPA saveAll() 插入数据库非常慢

java - Spring 3 MVC 和模态向导

java - 使用 Spring Cloud Sleuth 在每次 HTTP 请求后清除 MDC 上下文是否安全?

java - Spring JPA 不为实体创建表