{"id":1,"content":"Hello, World!"}
為 RESTful Web Service 啟用跨域請求
本指南將引導您完成使用 Spring 建立一個“Hello, World”RESTful Web Service 的過程,該服務在響應中包含跨源資源共享(CORS)的頭部。您可以在這篇部落格文章中找到更多關於 Spring CORS 支援的資訊。
您將構建什麼
您將構建一個接受傳送到 https://:8080/greeting
的 HTTP GET 請求的服務,並響應一個問候語的 JSON 表示,如下所示
您可以使用查詢字串中可選的 name
引數自定義問候語,如下所示
https://:8080/greeting?name=User
name
引數的值會覆蓋預設值 World
,並反映在響應中,如下所示
{"id":1,"content":"Hello, User!"}
此服務與構建 RESTful Web Service 中描述的服務略有不同,它使用了 Spring Framework 的 CORS 支援來新增相關的 CORS 響應頭部。
您需要準備什麼
-
大約 15 分鐘
-
您喜歡的文字編輯器或 IDE
-
Java 17 或更高版本
-
您也可以將程式碼直接匯入到您的 IDE 中
如何完成本指南
與大多數 Spring 入門指南一樣,您可以從頭開始完成每個步驟,或者跳過您已經熟悉的基本設定步驟。無論哪種方式,您最終都會得到可工作的程式碼。
要從頭開始,請繼續閱讀使用 Spring Initializr 開始。
要跳過基礎部分,請執行以下操作
-
下載並解壓本指南的原始碼倉庫,或使用 Git 克隆:
git clone https://github.com/spring-guides/gs-rest-service-cors.git
-
cd 到
gs-rest-service-cors/initial
-
跳至建立資源表示類。
完成時,您可以對照 gs-rest-service-cors/complete
中的程式碼檢查您的結果。
使用 Spring Initializr 開始
您可以使用此預初始化專案,然後單擊“生成”下載 ZIP 檔案。此專案已配置,適用於本教程中的示例。
手動初始化專案
-
導航到 https://start.spring.io。此服務將拉取應用程式所需的所有依賴項,併為您完成大部分設定。
-
選擇 Gradle 或 Maven 以及您想要使用的語言。本指南假設您選擇了 Java。
-
點選Dependencies(依賴項),然後選擇Spring Web。
-
點選Generate(生成)。
-
下載生成的 ZIP 檔案,它是根據您的選擇配置的 Web 應用程式存檔。
如果您的 IDE 集成了 Spring Initializr,您可以在 IDE 中完成此過程。 |
您也可以從 Github fork 專案並在您的 IDE 或其他編輯器中開啟它。 |
新增 httpclient5
依賴項
測試(在 complete/src/test/java/com/example/restservicecors/GreetingIntegrationTests.java
中)需要 Apache httpclient5
庫。
要將 Apache httpclient5
庫新增到 Maven,請新增以下依賴項
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<scope>test</scope>
</dependency>
下面的列表顯示了完成的 pom.xml
檔案
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>rest-service-cors-complete</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rest-service-cors-complete</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
要將 Apache httpclient5
庫新增到 Gradle,請新增以下依賴項
testImplementation 'org.apache.httpcomponents.client5:httpclient5'
下面的列表顯示了完成的 build.gradle
檔案
plugins {
id 'org.springframework.boot' version '3.3.0'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.apache.httpcomponents.client5:httpclient5'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
建立一個資源表示類
現在您已經設定好了專案和構建系統,您可以建立您的 Web Service 了。
透過思考服務互動開始這個過程。
該服務將處理傳送到 /greeting
的 GET
請求,可選地在查詢字串中包含一個 name
引數。GET
請求應該返回一個 200 OK
響應,並在主體中包含 JSON 以表示一個問候語。它應該類似於以下列表所示
{
"id": 1,
"content": "Hello, World!"
}
id
欄位是問候語的唯一識別符號,而 content
是問候語的文字表示。
為了建模問候語表示,建立一個資源表示類。提供一個帶有欄位、建構函式和訪問器的普通 Java 物件,用於處理 id
和 content
資料,如以下列表(來自 src/main/java/com/example/restservicecors/Greeting.java
)所示
package com.example.restservicecors;
public class Greeting {
private final long id;
private final String content;
public Greeting() {
this.id = -1;
this.content = "";
}
public Greeting(long id, String content) {
this.id = id;
this.content = content;
}
public long getId() {
return id;
}
public String getContent() {
return content;
}
}
Spring 使用 Jackson JSON 庫自動將 Greeting 型別的例項轉換為 JSON。 |
建立資源控制器
在 Spring 構建 RESTful Web Service 的方法中,HTTP 請求由控制器處理。這些元件很容易透過 @Controller
註解來識別,並且下面的列表(來自 src/main/java/com/example/restservicecors/GreetingController.java
)中顯示的 GreetingController
透過返回一個新的 Greeting
類例項來處理傳送到 /greeting
的 GET
請求
package com.example.restservicecors;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
@CrossOrigin(origins = "https://:9000")
@GetMapping("/greeting")
public Greeting greeting(@RequestParam(required = false, defaultValue = "World") String name) {
System.out.println("==== get greeting ====");
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
}
這個控制器簡潔明瞭,但在底層有很多事情正在發生。我們一步一步分解它。
@RequestMapping
註解確保傳送到 /greeting
的 HTTP 請求被對映到 greeting()
方法。
前面的例子使用了 @GetMapping 註解,它是 @RequestMapping(method = RequestMethod.GET) 的快捷方式。在這種情況下我們使用 GET 是因為它便於測試。Spring 仍然會拒絕來源與 CORS 配置不匹配的 GET 請求。瀏覽器不需要傳送 CORS 預檢請求,但如果我們想觸發預檢,可以使用 @PostMapping 並在請求主體中接受一些 JSON。 |
@RequestParam
將 name
查詢字串引數的值繫結到 greeting()
方法的 name
引數。此查詢字串引數不是 required
(必需)的。如果請求中缺少它,則使用 defaultValue
(預設值)World
。
方法體的實現建立並返回一個新的 Greeting
物件,其中 id
屬性的值基於 counter
的下一個值,content
的值基於查詢引數或預設值。它還使用問候語 template
來格式化給定的 name
。
傳統 MVC 控制器與前面所示的 RESTful Web Service 控制器之間的關鍵區別在於建立 HTTP 響應主體的方式。RESTful Web Service 控制器不是依賴檢視技術在伺服器端將問候語資料渲染為 HTML,而是填充並返回一個 Greeting
物件。物件資料直接作為 JSON 寫入 HTTP 響應。
為了實現這一點,@RestController
註解預設假定每個方法都繼承了 @ResponseBody
語義。因此,返回的物件資料被直接插入到響應主體中。
得益於 Spring 的 HTTP 訊息轉換器支援,Greeting
物件自然地被轉換為 JSON。由於 Jackson 位於類路徑中,Spring 的 MappingJackson2HttpMessageConverter
會自動被選擇來將 Greeting
例項轉換為 JSON。
啟用 CORS
您可以從單個控制器或全域性啟用跨域資源共享(CORS)。以下主題描述瞭如何進行
控制器方法 CORS 配置
為了讓 RESTful Web Service 在其響應中包含 CORS 訪問控制頭部,您必須向處理程式方法新增一個 @CrossOrigin
註解,如下列表(來自 src/main/java/com/example/restservicecors/GreetingController.java
)所示
@CrossOrigin(origins = "https://:9000")
@GetMapping("/greeting")
public Greeting greeting(@RequestParam(required = false, defaultValue = "World") String name) {
System.out.println("==== get greeting ====");
return new Greeting(counter.incrementAndGet(), String.format(template, name));
此 @CrossOrigin
註解僅為此特定方法啟用跨域資源共享。預設情況下,它允許所有來源、所有頭部以及 @RequestMapping
註解中指定的 HTTP 方法。此外,使用了 30 分鐘的 maxAge
。您可以透過指定以下註解屬性的值來定製此行為
-
origins(來源)
-
originPatterns(來源模式)
-
methods(方法)
-
allowedHeaders(允許的頭部)
-
exposedHeaders(暴露的頭部)
-
allowCredentials(允許憑據)
-
maxAge(最大時效)
.
在此示例中,我們只允許 https://:9000
傳送跨域請求。
您也可以在控制器類級別新增 @CrossOrigin 註解,以對該類的所有處理程式方法啟用 CORS。 |
全域性 CORS 配置
除了(或作為替代)細粒度的基於註解的配置外,您還可以定義一些全域性 CORS 配置。這類似於使用 Filter
,但可以在 Spring MVC 中宣告並與細粒度的 @CrossOrigin
配置結合使用。預設情況下,允許所有來源以及 GET
、HEAD
和 POST
方法。
以下列表(來自 src/main/java/com/example/restservicecors/GreetingController.java
)顯示了 GreetingController
類中的 greetingWithJavaconfig
方法
@GetMapping("/greeting-javaconfig")
public Greeting greetingWithJavaconfig(@RequestParam(required = false, defaultValue = "World") String name) {
System.out.println("==== in greeting ====");
return new Greeting(counter.incrementAndGet(), String.format(template, name));
greetingWithJavaconfig 方法和 greeting 方法(用於控制器級別 CORS 配置)之間的區別在於路由(/greeting-javaconfig 而不是 /greeting )以及 @CrossOrigin 來源的存在。 |
以下列表(來自 src/main/java/com/example/restservicecors/RestServiceCorsApplication.java
)顯示瞭如何在應用程式類中新增 CORS 對映
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/greeting-javaconfig").allowedOrigins("https://:9000");
}
};
}
您可以輕鬆更改任何屬性(例如示例中的 allowedOrigins
),並可以將此 CORS 配置應用於特定的路徑模式。
您可以結合全域性和控制器級別的 CORS 配置。 |
建立應用程式類
Spring Initializr 為您建立了一個最基礎的應用程式類。以下列表(來自 initial/src/main/java/com/example/restservicecors/RestServiceCorsApplication.java
)顯示了初始類
package com.example.restservicecors;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RestServiceCorsApplication {
public static void main(String[] args) {
SpringApplication.run(RestServiceCorsApplication.class, args);
}
}
您需要新增一個方法來配置如何處理跨源資源共享。以下列表(來自 complete/src/main/java/com/example/restservicecors/RestServiceCorsApplication.java
)顯示瞭如何進行
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/greeting-javaconfig").allowedOrigins("https://:9000");
}
};
}
以下列表顯示了完成的應用程式類
package com.example.restservicecors;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
public class RestServiceCorsApplication {
public static void main(String[] args) {
SpringApplication.run(RestServiceCorsApplication.class, args);
}
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/greeting-javaconfig").allowedOrigins("https://:9000");
}
};
}
}
@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 檔案,如下所示
如果您使用 Maven,您可以透過執行 ./mvnw spring-boot:run
來執行應用程式。另外,您可以使用 ./mvnw clean package
構建 JAR 檔案,然後執行 JAR 檔案,如下所示
此處描述的步驟建立了一個可執行的 JAR。您也可以構建一個經典的 WAR 檔案。 |
將顯示日誌輸出。服務應在幾秒鐘內啟動並執行。
測試服務
現在服務已啟動,請在瀏覽器中訪問 https://:8080/greeting
,您應該會看到
{"id":1,"content":"Hello, World!"}
透過訪問 https://:8080/greeting?name=User
提供一個 name
查詢字串引數。content
屬性的值從 Hello, World!
變為 Hello User!
,如下所示
{"id":2,"content":"Hello, User!"}
此更改表明 GreetingController
中的 @RequestParam
設定按預期工作。name
引數已獲得預設值 World
,但始終可以透過查詢字串顯式覆蓋。
此外,id
屬性已從 1
變為 2
。這證明您正在跨多個請求使用相同的 GreetingController
例項,並且其 counter
欄位在每次呼叫時都按預期遞增。
現在您可以測試 CORS 頭部是否就位,並允許來自另一個來源的 Javascript 客戶端訪問服務。為此,您需要建立一個 Javascript 客戶端來消費該服務。以下列表顯示了這樣一個客戶端
首先,建立一個名為 hello.js
的簡單 Javascript 檔案(來自 complete/public/hello.js
),內容如下
$(document).ready(function() {
$.ajax({
url: "https://:8080/greeting"
}).then(function(data, status, jqxhr) {
$('.greeting-id').append(data.id);
$('.greeting-content').append(data.content);
console.log(jqxhr);
});
});
此指令碼使用 jQuery 消費位於 https://:8080/greeting
的 REST 服務。它由 index.html
載入,如下列表(來自 complete/public/index.html
)所示
<!DOCTYPE html>
<html>
<head>
<title>Hello CORS</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="hello.js"></script>
</head>
<body>
<div>
<p class="greeting-id">The ID is </p>
<p class="greeting-content">The content is </p>
</div>
</body>
</html>
要測試 CORS 行為,您需要從另一個伺服器或埠啟動客戶端。這樣做不僅避免了兩個應用程式之間的衝突,還確保客戶端程式碼是從與服務不同的來源提供的。
要在本地主機埠 9000 上啟動客戶端,請保持應用程式在埠 8080 上執行,並在另一個終端中執行以下 Maven 命令
./mvnw spring-boot:run -Dspring-boot.run.jvmArguments='-Dserver.port=9000'
如果您使用 Gradle,可以使用此命令
./gradlew bootRun --args="--server.port=9000"
應用程式啟動後,在瀏覽器中開啟 https://:9000,您應該會看到以下內容,因為服務響應包含了相關的 CORS 頭部,所以 ID 和 content 被渲染到頁面中

現在,停止在埠 9000 執行的應用程式,保持應用程式在埠 8080 執行,並在另一個終端中執行以下 Maven 命令
./mvnw spring-boot:run -Dspring-boot.run.jvmArguments='-Dserver.port=9001'
如果您使用 Gradle,可以使用此命令
./gradlew bootRun --args="--server.port=9001"
應用程式啟動後,在瀏覽器中開啟 https://:9001,您應該會看到以下內容

在此,瀏覽器拒絕了請求,並且值未渲染到 DOM 中,因為缺少 CORS 頭部(或對於客戶端來說不足夠),因為我們只允許來自 https://:9000 的跨域請求,而不是 https://:9001。
總結
恭喜!您剛剛使用 Spring 開發了一個包含跨域資源共享的 RESTful Web Service。