精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

如何使用Spring構建REST服務(五)

「Spring」認證安全架構 ? 來源:「Spring」認證安全架構 ? 作者:「Spring」認證安全 ? 2022-07-28 16:03 ? 次閱讀

書接上文???

在 REST API 中構建鏈接

到目前為止,您已經使用基本鏈接構建了一個可進化的 API。為了發展您的 API 并更好地為您的客戶服務,您需要接受超媒體作為應用程序狀態引擎的概念。

這意味著什么?在本節中,您將詳細探討它。

業務邏輯不可避免地會建立涉及流程的規則。此類系統的風險在于我們經常將此類服務器端邏輯帶入客戶端并建立強耦合。REST 就是要打破這種連接并最小化這種耦合。

為了展示如何在不觸發客戶端中斷更改的情況下應對狀態變化,想象一下添加一個履行訂單的系統。

第一步,定義一條Order記錄:

鏈接
/src/main/java/payroll/Order.java

package payroll;import java.util.Objects;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.Id;import javax.persistence.Table;@Entity@Table(name = "CUSTOMER_ORDER")class Order {  private @Id @GeneratedValue Long id;  private String description;  private Status status;  Order() {}  Order(String description, Status status) {    this.description = description;    this.status = status;  }  public Long getId() {    return this.id;  }  public String getDescription() {    return this.description;  }  public Status getStatus() {    return this.status;  }  public void setId(Long id) {    this.id = id;  }  public void setDescription(String description) {    this.description = description;  }  public void setStatus(Status status) {    this.status = status;  }  @Override  public boolean equals(Object o) {    if (this == o)      return true;    if (!(o instanceof Order))      return false;    Order order = (Order) o;    return Objects.equals(this.id, order.id) && Objects.equals(this.description, order.description)        && this.status == order.status;  }  @Override  public int hashCode() {    return Objects.hash(this.id, this.description, this.status);  }  @Override  public String toString() {    return "Order{" + "id=" + this.id + ", description='" + this.description + '\'' + ", status=" + this.status + '}';  }}復制
  • 該類需要 JPA@Table注釋將表的名稱更改為,CUSTOMER_ORDER因為ORDER它不是表的有效名稱。
  • 它包括一個description字段以及一個status字段。

從客戶提交訂單到完成或取消訂單時,訂單必須經歷一系列狀態轉換。這可以捕獲為 Java enum

鏈接
/src/main/java/payroll/Status.java

package payroll;enum Status {  IN_PROGRESS, //  COMPLETED, //  CANCELLED}復制

enum捕獲了一個Order可以占據的各種狀態。對于本教程,讓我們保持簡單。

要支持與數據庫中的訂單交互,必須定義相應的 Spring Data 存儲庫:

Spring Data JPA 的JpaRepository基本接口

interface OrderRepository extends JpaRepository {}復制,>

有了這個,您現在可以定義一個基本的OrderController

鏈接
/src/main/java/payroll/OrderController.java

@RestControllerclass OrderController {  private final OrderRepository orderRepository;  private final OrderModelAssembler assembler;  OrderController(OrderRepository orderRepository, OrderModelAssembler assembler) {    this.orderRepository = orderRepository;    this.assembler = assembler;  }  @GetMapping("/orders")  CollectionModel> all() {    List> orders = orderRepository.findAll().stream() //        .map(assembler::toModel) //        .collect(Collectors.toList());    return CollectionModel.of(orders, //        linkTo(methodOn(OrderController.class).all()).withSelfRel());  }  @GetMapping("/orders/{id}")  EntityModel one(@PathVariable Long id) {    Order order = orderRepository.findById(id) //        .orElseThrow(() -> new OrderNotFoundException(id));    return assembler.toModel(order);  }  @PostMapping("/orders")  ResponseEntity> newOrder(@RequestBody Order order) {    order.setStatus(Status.IN_PROGRESS);    Order newOrder = orderRepository.save(order);    return ResponseEntity //        .created(linkTo(methodOn(OrderController.class).one(newOrder.getId())).toUri()) //        .body(assembler.toModel(newOrder));  }}復制
  • 它包含與您迄今為止構建的控制器相同的 REST 控制器設置。
  • 它同時注入OrderRepositorya 和 a (not yet built) OrderModelAssembler
  • 前兩個 Spring MVC 路由處理聚合根以及單個項目Order資源請求。
  • 第三條 Spring MVC 路由通過在IN_PROGRESS狀態中啟動它們來處理創建新訂單。
  • 所有控制器方法都返回 Spring HATEOAS 的RepresentationModel子類之一以正確呈現超媒體(或圍繞此類類型的包裝器)。

在構建 之前OrderModelAssembler,讓我們討論需要發生的事情。您正在對 、 和 之間的狀態流Status.IN_PROGRESS進行Status.COMPLETED建模Status.CANCELLED。向客戶端提供此類數據時,一件很自然的事情是讓客戶端根據此有效負載決定它可以做什么。

但那是錯誤的。

當您在此流程中引入新狀態時會發生什么?UI 上各種按鈕的放置可能是錯誤的。

如果您更改了每個州的名稱,可能是在編碼國際支持并顯示每個州的區域設置特定文本時會怎樣?這很可能會破壞所有客戶。

輸入HATEOAS超媒體作為應用程序狀態引擎。與其讓客戶端解析有效負載,不如為它們提供鏈接以發出有效操作的信號。將基于狀態的操作與數據負載分離。換句話說,當CANCELCOMPLETE是有效操作時,將它們動態添加到鏈接列表中。客戶端只需要在鏈接存在時向用戶顯示相應的按鈕。

這使客戶端不必知道此類操作何時有效,從而降低了服務器及其客戶端在狀態轉換邏輯上不同步的風險。

已經接受了 Spring HATEOAS
RepresentationModelAssembler組件的概念,將這樣的邏輯放入其中OrderModelAssembler將是捕獲此業務規則的完美位置:

鏈接
/src/main/java/payroll/OrderModelAssembler.java

package payroll;import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;import org.springframework.hateoas.EntityModel;import org.springframework.hateoas.server.RepresentationModelAssembler;import org.springframework.stereotype.Component;@Componentclass OrderModelAssembler implements RepresentationModelAssembler> {  @Override  public EntityModel toModel(Order order) {    // Unconditional links to single-item resource and aggregate root    EntityModel orderModel = EntityModel.of(order,        linkTo(methodOn(OrderController.class).one(order.getId())).withSelfRel(),        linkTo(methodOn(OrderController.class).all()).withRel("orders"));    // Conditional links based on state of the order    if (order.getStatus() == Status.IN_PROGRESS) {      orderModel.add(linkTo(methodOn(OrderController.class).cancel(order.getId())).withRel("cancel"));      orderModel.add(linkTo(methodOn(OrderController.class).complete(order.getId())).withRel("complete"));    }    return orderModel;  }}復制,>

此資源組裝器始終包含指向單項資源的自身鏈接以及返回聚合根的鏈接。但它也包括兩個條件鏈接OrderController.cancel(id)以及OrderController.complete(id)(尚未定義)。這些鏈接僅在訂單狀態為 時顯示Status.IN_PROGRESS

如果客戶可以采用 HAL 和讀取鏈接的能力,而不是簡單地讀取普通的舊 JSON 數據,他們可以交換對訂單系統領域知識的需求。這自然減少了客戶端和服務器之間的耦合。它打開了調整訂單履行流程的大門,而不會在流程中破壞客戶。

要完成訂單履行,請將以下內容添加到OrderController操作中cancel

在 OrderController 中創建“取消”操作

@DeleteMapping("/orders/{id}/cancel")ResponseEntity cancel(@PathVariable Long id) {  Order order = orderRepository.findById(id) //      .orElseThrow(() -> new OrderNotFoundException(id));  if (order.getStatus() == Status.IN_PROGRESS) {    order.setStatus(Status.CANCELLED);    return ResponseEntity.ok(assembler.toModel(orderRepository.save(order)));  }  return ResponseEntity //      .status(HttpStatus.METHOD_NOT_ALLOWED) //      .header(HttpHeaders.CONTENT_TYPE, MediaTypes.HTTP_PROBLEM_DETAILS_JSON_VALUE) //      .body(Problem.create() //          .withTitle("Method not allowed") //          .withDetail("You can't cancel an order that is in the " + order.getStatus() + " status"));}復制

Order它在允許取消之前檢查狀態。如果它不是一個有效的狀態,它會返回一個RFC-7807 Problem,一個支持超媒體的錯誤容器。如果轉換確實有效,則將 轉換OrderCANCELLED

并將其添加到OrderController訂單完成中:

在 OrderController 中創建“完整”操作

@PutMapping("/orders/{id}/complete")ResponseEntity complete(@PathVariable Long id) {  Order order = orderRepository.findById(id) //      .orElseThrow(() -> new OrderNotFoundException(id));  if (order.getStatus() == Status.IN_PROGRESS) {    order.setStatus(Status.COMPLETED);    return ResponseEntity.ok(assembler.toModel(orderRepository.save(order)));  }  return ResponseEntity //      .status(HttpStatus.METHOD_NOT_ALLOWED) //      .header(HttpHeaders.CONTENT_TYPE, MediaTypes.HTTP_PROBLEM_DETAILS_JSON_VALUE) //      .body(Problem.create() //          .withTitle("Method not allowed") //          .withDetail("You can't complete an order that is in the " + order.getStatus() + " status"));}復制

這實現了類似的邏輯以防止Order狀態完成,除非處于正確的狀態。

讓我們更新LoadDatabase以預加載一些Orders 以及Employee它之前加載的 s。

更新數據庫預加載器

package payroll;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.boot.CommandLineRunner;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationclass LoadDatabase {  private static final Logger log = LoggerFactory.getLogger(LoadDatabase.class);  @Bean  CommandLineRunner initDatabase(EmployeeRepository employeeRepository, OrderRepository orderRepository) {    return args -> {      employeeRepository.save(new Employee("Bilbo", "Baggins", "burglar"));      employeeRepository.save(new Employee("Frodo", "Baggins", "thief"));      employeeRepository.findAll().forEach(employee -> log.info("Preloaded " + employee));            orderRepository.save(new Order("MacBook Pro", Status.COMPLETED));      orderRepository.save(new Order("iPhone", Status.IN_PROGRESS));      orderRepository.findAll().forEach(order -> {        log.info("Preloaded " + order);      });          };  }}復制

現在你可以測試了!

要使用新生成的訂單服務,只需執行一些操作:

$ curl -v http://localhost:8080/orders{  “_嵌入”:{    “訂單”: [      {        “身份證”:3,        “描述”:“MacBook Pro”,        “狀態”:“已完成”,        “_鏈接”:{          “自己”: {            "href": "http://localhost:8080/orders/3"          },          “訂單”: {            "href": "http://localhost:8080/orders"          }        }      },      {        “身份證”:4,        “描述”:“iPhone”,        “狀態”:“IN_PROGRESS”,        “_鏈接”:{          “自己”: {            "href": "http://localhost:8080/orders/4"          },          “訂單”: {            "href": "http://localhost:8080/orders"          },          “取消”: {            "href": "http://localhost:8080/orders/4/cancel"          },          “完全的”: {            "href": "http://localhost:8080/orders/4/complete"          }        }      }    ]  },  “_鏈接”:{    “自己”: {      "href": "http://localhost:8080/orders"    }  }}

此 HAL 文檔會根據其當前狀態立即顯示每個訂單的不同鏈接。

  • 第一個訂單,即COMPLETED只有導航鏈接。未顯示狀態轉換鏈接。
  • 第二個訂單,即 IN_PROGRESS還具有取消鏈接和完整鏈接。

嘗試取消訂單:

$ curl -v -X 刪除 http://localhost:8080/orders/4/cancel> 刪除 /orders/4/cancel HTTP/1.1> 主機:本地主機:8080> 用戶代理:curl/7.54.0> 接受:*/*>< HTTP/1.1 200< 內容類型:application/hal+json;charset=UTF-8< 傳輸編碼:分塊< 日期:2018 年 8 月 27 日星期一 15:02:10 GMT<{  “身份證”:4,  “描述”:“iPhone”,  “狀態”:“取消”,  “_鏈接”:{    “自己”: {      "href": "http://localhost:8080/orders/4"    },    “訂單”: {      "href": "http://localhost:8080/orders"    }  }}

此響應顯示一個HTTP 200狀態代碼,表明它是成功的。響應 HAL 文檔顯示該訂單處于新狀態 ( CANCELLED)。改變狀態的鏈接消失了。

如果再次嘗試相同的操作……

$ curl -v -X 刪除 http://localhost:8080/orders/4/cancel* TCP_NODELAY 設置* 連接到 localhost (::1) 端口 8080 (#0)> 刪除 /orders/4/cancel HTTP/1.1> 主機:本地主機:8080> 用戶代理:curl/7.54.0> 接受:*/*>< HTTP/1.1 405< 內容類型:應用程序/問題+json< 傳輸編碼:分塊< 日期:2018 年 8 月 27 日星期一 15:03:24 GMT<{  "title": "方法不允許",  "detail": "您不能取消處于 CANCELED 狀態的訂單"}

…?您會看到HTTP 405 Method Not Allowed響應。DELETE已成為無效操作。Problem響應對象清楚地表明您不能“取消”已經處于“CANCELLED”狀態的訂單。

此外,嘗試完成相同的訂單也會失敗:

$ curl -v -X PUT localhost:8080/orders/4/complete* TCP_NODELAY 設置* 連接到 localhost (::1) 端口 8080 (#0)> PUT /orders/4/完成 HTTP/1.1> 主機:本地主機:8080> 用戶代理:curl/7.54.0> 接受:*/*>< HTTP/1.1 405< 內容類型:應用程序/問題+json< 傳輸編碼:分塊< 日期:2018 年 8 月 27 日星期一 15:05:40 GMT<{  "title": "方法不允許",  "detail": "您無法完成處于 CANCELED 狀態的訂單"}

有了這一切,您的訂單履行服務就能夠有條件地顯示可用的操作。它還可以防止無效操作。

通過利用超媒體和鏈接協議,客戶端可以構建得更堅固,并且不太可能僅僅因為數據的變化而崩潰。Spring HATEOAS 可以輕松構建您需要為客戶提供服務的超媒體。

概括

在本教程中,您使用了各種策略來構建 REST API。事實證明,REST 不僅僅是漂亮的 URI 和返回 JSON 而不是 XML。

相反,以下策略有助于降低您的服務破壞您可能控制或可能無法控制的現有客戶的可能性:

  • 不要刪除舊字段。相反,支持他們。
  • 使用基于 rel 的鏈接,這樣客戶端就不必擔心 URI 進行硬編碼。
  • 盡可能長時間地保留舊鏈接。即使您必須更改 URI,也要保留 rels,以便舊客戶端可以使用新功能。
  • 當各種狀態驅動操作可用時,使用鏈接而不是有效負載數據來指示客戶端。


RepresentationModelAssembler為每種資源類型構建實現并在所有控制器中使用這些組件似乎需要一些努力。但是這種額外的服務器端設置(感謝 Spring HATEOAS 使之變得容易)可以確保您控制的客戶端(更重要的是,您不控制的客戶端)可以隨著您的 API 隨著發展而輕松升級。

我們關于如何使用 Spring 構建 RESTful 服務員的教程到此結束。本教程的每個部分都在單個 github 存儲庫中作為單獨的子項目進行管理:

  • nonrest — 沒有自媒體的簡單 Spring MVC 應用程序
  • rest — Spring MVC + Spring HATEOAS 應用程序,每個資源的 HAL 表示
  • 進化- REST 應用程序,其中一個字段已進化但保留舊數據以實現向后兼容性
  • 鏈接- REST 應用程序,其中條件鏈接用于向客戶端發出有效狀態更改信號

要查看使用 Spring HATEOAS 的更多示例,請參閱Spring中國教育管理中心

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • spring
    +關注

    關注

    0

    文章

    338

    瀏覽量

    14310
  • REST
    +關注

    關注

    0

    文章

    32

    瀏覽量

    9398
收藏 人收藏

    評論

    相關推薦

    如何用ACM簡化你的Spring Cloud微服務環境配置管理

    摘要: 本文我們就如何使用阿里云ACM這樣的配置管理產品在Spring Cloud中替代Spring Cloud Config幫助簡化環境配置管理做一個簡單的示例,幫助你理解基于ACM來簡化微服務
    發表于 02-02 14:18

    使用阿里云ACM簡化你的Spring Cloud微服務環境配置管理

    摘要: 本文我們就如何使用阿里云ACM這樣的配置管理產品在Spring Cloud中替代Spring Cloud Config幫助簡化環境配置管理做一個簡單的示例,幫助你理解基于ACM來簡化微服務
    發表于 07-04 17:16

    Spring Boot嵌入式Web容器原理是什么

    Spring Boot嵌入式Web容器原理Spring Boot的目標是構建“非常容易創建、獨立、產品級別的基于Spring的應用”。這些應用是“立即可運行的”。在這個過程中,完全沒有
    發表于 12-16 07:57

    REST端口支持構建動態REST請求來使用RESTful API網絡

    REST端口支持構建動態REST請求來使用RESTful API網絡服務。 概覽 REST端口暴露了一個簡單的接口來為
    的頭像 發表于 01-17 09:11 ?4798次閱讀

    REST API是什么,如何使用REST端口

    /服務器) 模型對資源進行增刪改查操作。而其中客戶端和服務器是分離的,而知行之橋中的REST端口就是作為REST API中的客戶端,對服務
    的頭像 發表于 02-17 18:00 ?9191次閱讀
    <b class='flag-5'>REST</b> API是什么,如何使用<b class='flag-5'>REST</b>端口

    Spring REST Docs RESTful服務文檔

    ./oschina_soft/spring-restdocs.zip
    發表于 05-24 09:31 ?1次下載
    <b class='flag-5'>Spring</b> <b class='flag-5'>REST</b> Docs RESTful<b class='flag-5'>服務</b>文檔

    Spring認證是什么?

    ,例如:配置、組件掃描、AOP、數據訪問和事務、REST、安全、自動配置、執行器、 Spring boot測試等。
    的頭像 發表于 07-04 10:19 ?1279次閱讀
    <b class='flag-5'>Spring</b>認證是什么?

    如何獲得Spring認證?學習JAVA如何獲得Spring Professional認證?

    、組件掃描、AOP、數據訪問和事務、REST、安全、自動配置、執行器、 Spring boot測試等。 1)參加Spring中國教育管理中心授權合作伙伴Spring培訓課程 2)報名考
    的頭像 發表于 07-04 10:20 ?1745次閱讀
    如何獲得<b class='flag-5'>Spring</b>認證?學習JAVA如何獲得<b class='flag-5'>Spring</b> Professional認證?

    spring認證證書有用嗎?

    :配置、組件掃描、AOP、數據訪問和事務、REST、安全、自動配置、執行器、 Spring boot測試等。 目前Spring認證的版本:Spring v5.0(VMware EDU-
    的頭像 發表于 07-12 15:59 ?2189次閱讀

    如何使用Spring構建REST服務(一)

    關于 REST 如何適應微服務世界還有一個更大的討論,但是——對于本教程——讓我們看看構建 RESTful 服務
    的頭像 發表于 07-28 15:59 ?888次閱讀

    如何使用Spring構建REST服務(二)

    要使用 Web 層次包裝您的存儲庫,您必須使用 Spring MVC。多虧了 Spring Boot,代碼基礎設施很少。相反,我們可以專注于行動。
    的頭像 發表于 07-28 16:00 ?715次閱讀

    如何使用Spring構建REST服務(三)

    到目前為止,您擁有一個基于 Web 服務來處理涉及員工數據的核心操作。但這還不足以讓事情變得“RESTful”。
    的頭像 發表于 07-28 16:01 ?811次閱讀

    如何使用Spring構建REST服務(四)

    通過一個額外的庫和幾行額外的代碼,您已將超媒體添加到您的應用程序中。但這并不是使您的服務成為 RESTful 所需的唯一事情。REST 的一個重要方面是它既不是技術堆棧也不是單一標準。
    的頭像 發表于 07-28 16:02 ?728次閱讀

    REST的6大指導原則

    systems )架構風格。由Roy Fielding 提出。 REST API 也稱RESTful API, 其遵循REST架構規范的應用編程接口, 支持與RESTful WEB服務進行交互。簡單來講就是
    的頭像 發表于 10-09 14:27 ?1492次閱讀

    Spring Cloud :打造可擴展的微服務網關

    Spring Cloud Gateway是一個基于Spring Framework 5和Project Reactor的反應式編程模型的微服務網關。它提供了豐富的功能,包括動態路由、請求限流、集成安全性等,使其成為
    的頭像 發表于 10-22 10:03 ?499次閱讀
    <b class='flag-5'>Spring</b> Cloud :打造可擴展的微<b class='flag-5'>服務</b>網關