建構 Hypermedia 驅動的 RESTful Web 服務

本指南將引導您使用 Spring 建立「Hello, World」Hypermedia 驅動的 REST Web 服務。

Hypermedia 是 REST 的重要面向。它讓您建構服務,大幅度地解耦用戶端和伺服器,並讓它們獨立演進。為 REST 資源傳回的表示形式不僅包含資料,也包含相關資源的連結。因此,表示形式的設計對於整體服務的設計至關重要。

您將建構什麼

您將使用 Spring HATEOAS 建構一個 hypermedia 驅動的 REST 服務:一個 API 函式庫,您可以使用它來建立指向 Spring MVC 控制器的連結、建構資源表示形式,並控制它們如何呈現為支援的 hypermedia 格式(例如 HAL)。

此服務將接受位於 https://127.0.0.1:8080/greeting 的 HTTP GET 請求。

它將以 JSON 格式回應問候語,其中包含最簡單的 hypermedia 元素,即指向資源本身的連結。以下列表顯示了輸出

{
  "content":"Hello, World!",
  "_links":{
    "self":{
      "href":"https://127.0.0.1:8080/greeting?name=World"
    }
  }
}

回應已指出您可以使用查詢字串中的選用 name 參數自訂問候語,如下列列表所示

https://127.0.0.1:8080/greeting?name=User

name 參數值會覆寫預設值 World,並反映在回應中,如下列列表所示

{
  "content":"Hello, User!",
  "_links":{
    "self":{
      "href":"https://127.0.0.1:8080/greeting?name=User"
    }
  }
}

您需要的東西

如何完成本指南

與大多數 Spring 開始使用指南 一樣,您可以從頭開始並完成每個步驟,或者您可以跳過您已經熟悉的基本設定步驟。無論哪種方式,您最終都會得到可運作的程式碼。

若要從頭開始,請繼續前往 從 Spring Initializr 開始

若要跳過基本步驟,請執行以下操作

當您完成時,您可以將您的結果與 gs-rest-hateoas/complete 中的程式碼進行比較。

從 Spring Initializr 開始

您可以使用此 預先初始化的專案,然後按一下 Generate 以下載 ZIP 檔案。此專案已設定為符合本教學課程中的範例。

若要手動初始化專案

  1. 導覽至 https://start.spring.io。此服務會提取應用程式所需的所有相依性,並為您完成大部分的設定。

  2. 選擇 Gradle 或 Maven 以及您想要使用的語言。本指南假設您選擇了 Java。

  3. 按一下Dependencies 並選取 Spring HATEOAS

  4. 按一下Generate

  5. 下載產生的 ZIP 檔案,這是一個使用您的選擇設定的 Web 應用程式的封存檔。

如果您的 IDE 具有 Spring Initializr 整合,您可以從您的 IDE 完成此程序。
您也可以從 Github fork 此專案,並在您的 IDE 或其他編輯器中開啟它。

建立資源表示類別

現在您已經設定了專案和建置系統,您可以建立您的 Web 服務。

從思考服務互動開始此流程。

此服務將在 /greeting 公開一個資源,以處理 GET 請求,可選擇性地在查詢字串中使用 name 參數。GET 請求應傳回 200 OK 回應,並在主體中使用 JSON 來表示問候語。

除此之外,資源的 JSON 表示形式將在 _links 屬性中加入 hypermedia 元素列表。最基本的形式是指向資源本身的連結。表示形式應類似於以下列表

{
  "content":"Hello, World!",
  "_links":{
    "self":{
      "href":"https://127.0.0.1:8080/greeting?name=World"
    }
  }
}

content 是問候語的文字表示形式。_links 元素包含連結列表(在本例中,只有一個,其關係類型為 rel,且 href 屬性指向被存取的資源)。

為了對問候語表示形式進行建模,請建立一個資源表示類別。由於 _links 屬性是表示模型的基本屬性,因此 Spring HATEOAS 隨附一個基底類別(稱為 RepresentationModel),可讓您新增 Link 的執行個體,並確保它們如先前所示呈現。

建立一個擴展 RepresentationModel 的簡單 Java 物件,並為內容以及建構函式新增欄位和存取器,如下列列表(來自 src/main/java/com/example/resthateoas/Greeting.java)所示

package com.example.resthateoas;

import org.springframework.hateoas.RepresentationModel;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public class Greeting extends RepresentationModel<Greeting> {

	private final String content;

	@JsonCreator
	public Greeting(@JsonProperty("content") String content) {
		this.content = content;
	}

	public String getContent() {
		return content;
	}
}
  • @JsonCreator:表示 Jackson 如何建立此 POJO 的執行個體。

  • @JsonProperty:標記 Jackson 應將此建構函式引數放入的欄位。

正如您將在本指南稍後看到的,Spring 將使用 Jackson JSON 函式庫自動將 Greeting 類型的執行個體封送處理為 JSON。

接下來,建立將提供這些問候語的資源控制器。

建立 REST 控制器

在 Spring 建構 RESTful Web 服務的方法中,HTTP 請求由控制器處理。這些元件由 @RestController 註解識別,它結合了 @Controller@ResponseBody 註解。以下 GreetingController(來自 src/main/java/com/example/resthateoas/GreetingController.java)透過傳回 Greeting 類別的新執行個體來處理 /greetingGET 請求

package com.example.resthateoas;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@RestController
public class GreetingController {

	private static final String TEMPLATE = "Hello, %s!";

	@RequestMapping("/greeting")
	public HttpEntity<Greeting> greeting(
		@RequestParam(value = "name", defaultValue = "World") String name) {

		Greeting greeting = new Greeting(String.format(TEMPLATE, name));
		greeting.add(linkTo(methodOn(GreetingController.class).greeting(name)).withSelfRel());

		return new ResponseEntity<>(greeting, HttpStatus.OK);
	}
}

此控制器簡潔而簡單,但有很多事情正在發生。我們逐步分解它。

@RequestMapping 註解確保對 /greeting 的 HTTP 請求會對應到 greeting() 方法。

以上範例未指定 GETPUTPOST 等,因為 @RequestMapping 預設會對應所有 HTTP 操作。使用 @GetMapping("/greeting") 來縮小此對應範圍。在這種情況下,您也需要 import org.springframework.web.bind.annotation.GetMapping;

@RequestParam 將查詢字串參數 name 的值繫結到 greeting() 方法的 name 參數。由於使用了 defaultValue 屬性,因此此查詢字串參數隱含地並非 required。如果請求中不存在,則使用 defaultValue World

由於類別上存在 @RestController 註解,因此會將隱含的 @ResponseBody 註解新增至 greeting 方法。這會導致 Spring MVC 將傳回的 HttpEntity 及其酬載 (Greeting) 直接呈現給回應。

方法實作中最有趣的部分是如何建立指向控制器方法的連結,以及如何將其新增至表示模型。linkTo(…)methodOn(…) 都是 ControllerLinkBuilder 上的靜態方法,可讓您模擬對控制器的呼叫方法。傳回的 LinkBuilder 將檢查控制器方法的對應註解,以建構方法對應到的確切 URI。

Spring HATEOAS 尊重各種 X-FORWARDED- 標頭。如果您將 Spring HATEOAS 服務置於 Proxy 後方,並使用 X-FORWARDED-HOST 標頭正確設定它,則產生的連結將會正確格式化。

withSelfRel() 的呼叫會建立一個 Link 執行個體,您將其新增至 Greeting 表示模型。

@SpringBootApplication 是一個方便的註解,它新增了以下所有內容

  • @Configuration:將類別標記為應用程式內容的 Bean 定義來源。

  • @EnableAutoConfiguration:告知 Spring Boot 開始根據類別路徑設定、其他 Bean 和各種屬性設定新增 Bean。例如,如果 spring-webmvc 在類別路徑上,則此註解會將應用程式標記為 Web 應用程式,並啟動關鍵行為,例如設定 DispatcherServlet

  • @ComponentScan:告知 Spring 在 com/example 套件中尋找其他元件、組態和服務,使其找到控制器。

main() 方法使用 Spring Boot 的 SpringApplication.run() 方法來啟動應用程式。您是否注意到沒有單行 XML?也沒有 web.xml 檔案。此 Web 應用程式是 100% 純 Java,您不必處理任何管線或基礎架構的設定。

建置可執行 JAR 檔

您可以使用 Gradle 或 Maven 從命令列執行應用程式。您也可以建置一個包含所有必要相依性、類別和資源的單個可執行 JAR 檔並執行它。建置可執行 JAR 檔可以輕鬆地在整個開發生命週期、跨不同環境等情況下,將服務作為應用程式運送、版本控制和部署。

如果您使用 Gradle,您可以使用 ./gradlew bootRun 來執行應用程式。或者,您可以使用 ./gradlew build 來建置 JAR 檔,然後執行 JAR 檔,如下所示

java -jar build/libs/gs-rest-hateoas-0.1.0.jar

如果您使用 Maven,您可以使用 ./mvnw spring-boot:run 來執行應用程式。或者,您可以使用 ./mvnw clean package 來建置 JAR 檔,然後執行 JAR 檔,如下所示

java -jar target/gs-rest-hateoas-0.1.0.jar
此處描述的步驟會建立可執行 JAR。您也可以 建置傳統 WAR 檔

將顯示記錄輸出。服務應在幾秒鐘內啟動並執行。

測試服務

現在服務已啟動,請造訪 https://127.0.0.1:8080/greeting,您應該會看到以下內容

{
  "content":"Hello, World!",
  "_links":{
    "self":{
      "href":"https://127.0.0.1:8080/greeting?name=World"
    }
  }
}

透過造訪以下 URL 提供 name 查詢字串參數:https://127.0.0.1:8080/greeting?name=User。請注意 content 屬性的值如何從 Hello, World! 變更為 Hello, User!,並且 self 連結的 href 屬性也反映了該變更,如下列列表所示

{
  "content":"Hello, User!",
  "_links":{
    "self":{
      "href":"https://127.0.0.1:8080/greeting?name=User"
    }
  }
}

此變更示範了 GreetingController 中的 @RequestParam 配置如預期般運作。name 參數已給定預設值 World,但始終可以透過查詢字串明確覆寫。

摘要

恭喜!您剛剛使用 Spring HATEOAS 開發了一個 hypermedia 驅動的 RESTful Web 服務。

取得程式碼