在 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
时,我都应该让客户端将整个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 将它们映射到表。
【问题讨论】:
【参考方案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 的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Ember Data 1.13.7 中创建和保存新模型并更新 UI
在 asp net core MVC 应用程序中创建和验证 JWT 令牌(没有客户端服务器方法)