简体   繁体   English

Springboot Rest(ful) Api 和查找值

[英]Springboot Rest(ful) Api and lookup values

I want to write some Rest(ful) application with Spring Boot and Spring Data JPA.我想用 Spring Boot 和 Spring Data JPA 编写一些 Rest(ful) 应用程序。

Let's assume that for business reasons I have a database with the following tables:假设出于业务原因,我有一个包含下表的数据库:

customer(id number, first_name text, last_name text, type text);
customer_type(type text, description text);

where:在哪里:

  • id is generated by the database at inserion time id由数据库在插入时生成
  • type column in customer table is a foreign key to type column in customer_type table and it is immutable from a microservice point of view, just a lookup table. customer_type表中的type列是customer表中type列的外键,从微服务的角度来看它是不可变的,只是一个查找表。

Assuming I want to create APIs for CRUD operations on a customer but want to minimize api calls when just reading, I suppose I need the following operations:假设我想为客户的 CRUD 操作创建 API,但希望在阅读时尽量减少 api 调用,我想我需要以下操作:

  • GET /customer/{id}获取/客户/{id}
  • POST /customer邮寄/客户
  • PUT /customer/{id} PUT /客户/{id}
  • DELETE /customer/{id}删除/客户/{id}

How the body should be structured?身体应该如何构造? For GET operation the response should be对于 GET 操作,响应应该是

{
   "id":123,
   "firstName":"John",
   "lastName":"Doe",
   "customerType":{
      "type":"P",
      "description":"Premium Customer"
   }
}

But for POST I imagine I need to avoid sending the id and send just the customer type since the description is immutable and the client needs the description only for visualizing the information on screen, but this leads to different request body from the one returned in the GET operation.但是对于 POST,我想我需要避免发送 id 而只发送客户类型,因为描述是不可变的,并且客户只需要描述来在屏幕上可视化信息,但这会导致请求正文与返回的请求正文不同获取操作。

For the PUT operation is the same but also should the id field be sent?对于 PUT 操作是一样的但也应该发送id字段吗? How to handle the case where the id in the API path is different from the id in the request body if sent?发送时API路径中的id与请求体中的id不一致如何处理?

DELETE should not be a problem since it just deletes the row in customer table. DELETE 应该不是问题,因为它只是删除customer表中的行。

Thank you谢谢

How the body should be structured?身体应该如何构造?

Let's make a step back first and let us discuss quickly what you basically try when following a REST architecture and why and how REST installs those mechanisms.让我们先退后一步,让我们快速讨论在遵循 REST 体系结构时您基本上会尝试什么,以及 REST 为什么以及如何安装这些机制。

REST is an architectural style that helps in decoupling clients from servers by introducing indirection mechanisms which may seem odd at first but in the end allow you to achieve the required level of decoupling which allows clients to introduce changes which clients will naturally adept to. REST 是一种架构风格,它通过引入间接机制帮助将客户端与服务器解耦,这起初看起来很奇怪,但最终允许您达到所需的解耦级别,允许客户端引入客户端自然会适应的更改。 Such indirection mechanisms include attaching URIs to link-relation names, using form-based representation formats to tell a client how to create requests, content-type negotiation to return representations supported and understood by others and so forth.这种间接机制包括将 URI 附加到链接关系名称、使用基于形式的表示格式来告诉客户端如何创建请求、内容类型协商以返回其他人支持和理解的表示等。 If you don't need such properties, ie as client and servers always go hand in hand in regards to changes and communicate on predefined messages, REST is probably not the best style to follow.如果您不需要这样的属性,即客户端和服务器始终 go 在更改方面手拉手并根据预定义消息进行通信,则 REST 可能不是最好的样式。 If you though have a server that is contacted by various clients not under your control or a client that has to contact various servers, also not under your direct control, this is where REST truly starts to shine if all parties adhere to these concepts.如果您有一个由不受您控制的各种客户端联系的服务器,或者有一个客户端必须联系各种服务器,但也不受您的直接控制,那么如果所有各方都遵守这些概念,这就是 REST 真正开始发光的地方。

One of RESTs premise is that a server will teach clients everything they need to know in order to construct requests. REST 的前提之一是服务器将向客户传授他们构建请求所需知道的一切。 If you look at the Web, where HTML is basically used everywhere, you might see that HTML defines HTML forms which basically allow a server to explain to a client what properties of a resource the server expects as input.如果您查看 Web,其中 HTML 基本上无处不在,您可能会看到 HTML 定义了HTML forms ,它基本上允许服务器向客户端解释服务器期望作为输入的资源的哪些属性。 On top of that the form also tells you client which HTTP operation to use, which target URI to send the request to and which media-type to represent the state in. In HTML this is usually implicitly given as application/x-www-form-urlencoded which chains properties together ie like this:最重要的是,该表单还告诉您客户端使用哪个 HTTP 操作,将请求发送到哪个目标 URI 以及代表 state 的媒体类型。在 HTML 中,这通常隐式给出为application/x-www-form-urlencoded将属性链接在一起,如下所示:

firstName=Roman&lastName=Vottner&role=Dev

or the like.等等。 This is in essence what HATEOAS or hypertext as the engine of application state is all about.这本质上就是 HATEOAS 或hypertext as the engine of application state全部内容。 You use in-build controls of the media-type exchanged to allow your client to progress its task instead of having to consult external documentation to lookup the "API" of some services.您使用交换的媒体类型的内置控件来允许您的客户端推进其任务,而不必查阅外部文档来查找某些服务的“API”。 Ie a form could state that an input only allows numeric values, that a sub-portion of the form represents a date/time picker widget which a client could render to a user accordingly, or an element represents a slider with a given range of admissible values and the like.即一个表单可以 state 输入只允许数值,表单的子部分表示客户端可以相应地呈现给用户的日期/时间选择器小部件,或者元素表示 slider 具有给定的可接受范围价值观之类的。

How the actual representation format you have to send to the server has to look like depends on the instructed media-type.您必须发送到服务器的实际表示格式如何取决于指示的媒体类型。 Ie HAL forms uses application/json by default and also specifies that application/x-www-form-urlencoded needs to be supported.HAL forms默认使用application/json ,还指定需要支持application/x-www-form-urlencoded Other media-types have explicitly negotiated between client and server.其他媒体类型已在客户端和服务器之间明确协商。 Ion states that application/json or application/ion+json have to be negotiated via the Content-Type request header. Ion指出application/jsonapplication/ion+json必须通过Content-Type请求 header 进行协商。

In plain application/json the url-encoded payload from above could simply be expressed as:在普通的application/json中,上面的 url 编码有效负载可以简单地表示为:

{
    "firstName": "Roman",
    "lastName": "Vottner",
    "role": "Dev"
}

and this is OK as the server basically instructed you to send this data in that format.这没关系,因为服务器基本上指示您以该格式发送此数据。

There are further media-types available that are worth a closer look whether they could fit your need or not.还有更多可用的媒体类型,无论它们是否满足您的需要,都值得仔细研究。 Ie Hydra has a bit of a different take on this matter by connecting Linked Data to REST and its affordances called operations and allows to describe resources and its properties through LD classes.Hydra通过将关联数据连接到 REST 及其称为操作的可供性,并允许通过 LD 类描述资源及其属性,对此事采取了一些不同的看法。 So the presence of an affordance for a certain resource tells you what you can do with that resource, like ie updating its state, and therefore also which class it belongs to and therefore which properties it has.因此,某个资源的可供性的存在告诉你你可以用那个资源做什么,比如更新它的 state,因此还有它属于哪个 class 以及它有哪些属性。

This just should illustrate how a negotiated media type finally decides how the actual representation needs to look like that has to be sent to the server.这只是应该说明协商的媒体类型如何最终决定实际表示需要如何看起来必须发送到服务器。

In regards of whether to put in resource identifiers in the payload or not it depends.至于是否在有效负载中放入资源标识符,这取决于它。 Usually resources are identified by the URI/IRI and this, as a whole, is the identifier of the resource.通常资源由 URI/IRI 标识,作为一个整体,这是资源的标识符。 In your application though you will reference related domain objects through their ID which does not necessarily need to be, and probably also should not be, part of the IRI itself.在您的应用程序中,虽然您将通过它们的 ID 引用相关的域对象,但这些 ID 不一定需要并且可能也不应该是 IRI 本身的一部分。 Ie let's assume we retrieve a resource that represents an order.即让我们假设我们检索代表订单的资源。 That order contains the users name and address, the various items that got ordered including some meta data describing those items and what not.该订单包含用户姓名和地址,订购的各种物品,包括一些描述这些物品的元数据和其他物品。 It usually makes sense in such a case to add the orderId which you use in your application even though the URI may contain that information already.在这种情况下,添加您在应用程序中使用的 orderId 通常是有意义的,即使 URI 可能已经包含该信息。 Users of that API are usually not interested in those URIs but the actual content and might also never see those URIs if they are hidden behind automated processes or user interfaces.该 API 的用户通常对这些 URI 不感兴趣,而是对实际内容感兴趣,如果这些 URI 隐藏在自动化流程或用户界面后面,他们也可能永远看不到这些 URI。 If a user now wants to print out that order s/he has all the information needed to file complaints later on via phone ie In other cases, ie if you design a resource to be an all-purpose clipboard like, copy&paste location, an ID does not make any sense unless you grant the user to explicitly reference one of that states directly.如果用户现在想打印出该订单,他/她拥有稍后通过电话提出投诉所需的所有信息,即在其他情况下,即如果您将资源设计为通用剪贴板,例如复制和粘贴位置,ID没有任何意义,除非您授予用户直接明确引用其中一个状态。

The reason why IDs should not be part of the URI itself stems from the fact that a URI shouldn't change if the actual resource does not change. ID 不应该是 URI 本身的一部分的原因是,如果实际资源不改变,URI 不应该改变。 Ie we have a customer who went through a merge a couple of years ago.也就是说,我们有一位客户在几年前经历了一次合并。 They used to expose all their products via own URIs that exposed the productId as part of the URI.他们过去常常通过自己的 URI 公开所有产品,这些 URI 将 productId 作为 URI 的一部分公开。 During the merger the tried to combine the various different data models to reduce the number of systems they had to operate while serving each of their customers with the same data as before as the underlying products didn't change.在合并期间,他们试图结合各种不同的数据模型以减少他们必须运行的系统数量,同时为他们的每个客户提供与以前相同的数据,因为基础产品没有改变。 As they tried to stay "backwards" compatible for the purpose of supporting legacy systems of their customers, they quickly noticed that exposing those productIds as part of the URI was causing them some troubles.当他们试图保持“向后”兼容以支持其客户的遗留系统时,他们很快注意到将这些产品 ID 作为 URI 的一部分公开给他们带来了一些麻烦。 If they had used a mapping table of ie exposed UUIDs to internal productIds (again an introduction of indirection) earlier they could have reduced their whole data model and thus complexity by a lot while being able to change the mapping from internal prodcutId to UUID on the fly while allowing their clients to lookup the product information.如果他们早些时候使用了一个映射表,即暴露的 UUID 到内部 productId(再次引入间接),他们可以减少他们的整个数据 model,从而减少复杂性,同时能够改变从内部 prodcutId 到 UUID 的映射飞行,同时允许他们的客户查找产品信息。

Long story short, as hopefully can be seen the structure of a representation depends on the exchanged media type.长话短说,希望可以看出表示的结构取决于交换的媒体类型。 There are loads of different media-types available.有许多不同的 媒体类型可用。 Use the ones that allow you to describe resources to clients, such as HAL/HAL forms, Ion, Hydra, .... In regards to URIs, don't overengineer URIs.使用允许您向客户端描述资源的资源,例如 HAL/HAL forms、Ion、Hydra……关于 URI,不要过度设计 URI。 They are, as a whole, just a pointer to a resource and clients are usually interested in the content, not the URI, As such, make use of indirection-features like link-relation names.它们作为一个整体只是指向资源的指针,客户通常对内容感兴趣,而不是 URI。因此,使用链接关系名称等间接功能。 content-type negotiation and so forth to help remove the direct coupling of clients to services but instead rely more on the document type exchanged.内容类型协商等有助于消除客户端与服务的直接耦合,而是更多地依赖于交换的文档类型。 The media-type here becomes basically the contract of the message.这里的媒体类型基本上成为消息的契约。 Through mappings on the client and server side resources of various representations can be "translated" to an object which you can use in your application.通过在客户端和服务器端的映射,各种表示形式的资源可以“转换”为 object,您可以在您的应用程序中使用它。

As you've tagged your question with and , you might want to look into spring-hateoas .当您用标记您的问题时,您可能想查看spring-hateoas It supports HAL out of the box, HAL forms can be used via affordances though the media-type needs to be enabled explicitly for it otherwise you might miss out on the form-template in the responses.它支持开箱即用的 HAL,HAL forms 可以通过 affordances 使用,但需要为其显式启用媒体类型,否则您可能会错过响应中的表单模板。 Hydra support in spring-hateoas seems to be added through hydra-java which implements the Spring HATEOAS SPI.似乎通过实现 Spring HATEOAS SPI 的hydra-java添加了spring-hateoas中的 Hydra 支持。 While Amazon provides implementation for Ion for various programming languages, including Java, it does not yet support Spring HATEOAS or Spring in general.虽然Amazon为各种编程语言(包括 Java)提供了 Ion 的实现,但它通常还不支持 Spring HATEOAS 或 Spring。 Here a custom SPI implementation may be necessary.这里可能需要自定义 SPI 实现。

For PUT operations you need to send the id of the entity that you want to update.对于 PUT 操作,您需要发送要更新的实体的 ID。

If you want to generate the same response as you would get in GET, then you need to write a DTO and map details accordingly.如果您想生成与在 GET 中获得的响应相同的响应,则需要相应地编写 DTO 和 map 详细信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM