我正在使用 Spring Boot 从头开始开发我的第一个 RESTful API。
我已经为“独立”实体创建了端点、模型和 JPA 存储库。但现在我开始将它们联系在一起,并在做了一些研究后,我得出的结论是我可能必须创建 DTO。我不认为每次使用 POST 请求创建新的 Order
时,我都应该让客户端发送整个 Customer
和 Employee
对象在请求中作为 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 - 顶层,实体 - 通常是最低层,数据层) 。有很多例子可以证明这一点(例如 here 和 here )
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/