{"id":1,"content":"Hello, World!"}REST API で CORS を有効化
このガイドでは、レスポンスに Cross-Origin Resource Sharing(CORS)のヘッダーを含む Spring で "Hello, World" RESTful Web サービスを作成するプロセスを説明します。Spring CORS サポートの詳細については、このブログ投稿を参照してください (英語) 。
構築するもの
次のように、http://localhost:8080/greeting で HTTP GET リクエストを受け入れ、グリーティングの JSON 表現で応答するサービスを構築します。
次のように、クエリ文字列のオプションの name パラメーターを使用して、グリーティングをカスタマイズできます。
http://localhost:8080/greeting?name=Username パラメーター値は、次のように、World のデフォルト値をオーバーライドし、レスポンスに反映されます。
{"id":1,"content":"Hello, User!"}このサービスは、Spring Framework CORS サポートを使用して関連する CORS レスポンスヘッダーを追加するという点で、REST API の作成で説明されているサービスとは少し異なります。
必要なもの
約 15 分
Eclipse STS や IntelliJ IDEA のような任意の IDE または VSCode のようなテキストエディター
Java 17 以降
コードを直接 IDE にインポートすることもできます。
本ガイドの完成までの流れ
ほとんどの Spring 入門ガイドと同様に、最初から始めて各ステップを完了するか、すでに慣れている場合は基本的なセットアップステップをバイパスできます。いずれにしても、最終的に動作するコードになります。
最初から始めるには、Spring Initializr から開始に進みます。
基本をスキップするには、次の手順を実行します。
このガイドを Eclipse で「Spring 入門コンテンツのインポート」するか、ソースリポジトリをダウンロードして解凍、または、Git (英語) を使用してクローンを作成します。
git clone https://github.com/spring-guides/gs-rest-service-cors.gitgs-rest-service-cors/initialに cdリソース表現クラスを作成するにジャンプしてください。
完了したときは、gs-rest-service-cors/complete のコードに対して結果を確認できます。
Spring Initializr から開始
IDE を使用する場合はプロジェクト作成ウィザードを使用します。IDE を使用せずにコマンドラインなどで開発する場合は、この事前に初期化されたプロジェクトからプロジェクトを ZIP ファイルとしてダウンロードできます。このプロジェクトは、このチュートリアルの例に合うように構成されています。
プロジェクトを手動で初期化するには:
IDE のメニューまたはブラウザーから Spring Initializr を開きます。アプリケーションに必要なすべての依存関係を取り込み、ほとんどのセットアップを行います。
Gradle または Maven のいずれかと、使用する言語を選択してください。
依存関係をクリックして、Spring Web を選択します。
生成をクリックします。
結果の ZIP ファイルをダウンロードします。これは、選択して構成された Web アプリケーションのアーカイブです。
| Eclipse や IntelliJ のような IDE は新規プロジェクト作成ウィザードから Spring Initializr の機能が使用できるため、手動での ZIP ファイルのダウンロードやインポートは不要です。 |
| プロジェクトを GitHub からフォークして、IDE または他のエディターで開くこともできます。 |
httpclient5 依存関係の追加
テストには 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>4.0.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>rest-service-cors-complete</artifactId>
<version>0.0.1-SNAPSHOT</version>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-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'testImplementation("org.apache.httpcomponents.client5:httpclient5') 次のリストは、完成した build.gradle(.kts) ファイルを示しています。
plugins {
id 'org.springframework.boot' version '4.0.1'
id 'io.spring.dependency-management' version '1.1.7'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(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'
testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
}
test {
useJUnitPlatform()
}plugins {
id("org.springframework.boot") version "4.0.1"
id("io.spring.dependency-management") version "1.1.7"
kotlin("jvm") version "2.2.21"
kotlin("plugin.spring") version "2.2.21"
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
testImplementation("org.apache.httpcomponents.client5:httpclient5")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.boot:spring-boot-starter-webmvc-test")
}
kotlin {
compilerOptions {
freeCompilerArgs.addAll("-Xjsr305=strict", "-Xannotation-default-target=param-property")
}
}
tasks.withType<Test> {
useJUnitPlatform()
}リソース表現クラスを作成する
プロジェクトとビルドシステムをセットアップしたため、Web サービスを作成できます。
サービスの相互作用について考えることでプロセスを開始します。
サービスは、/greeting への GET リクエストを処理します。オプションで、クエリ文字列に name パラメーターを使用します。GET リクエストは、あいさつを表すために本文に JSON を含む 200 OK レスポンスを返す必要があります。次のようになります。
{
"id": 1,
"content": "Hello, World!"
}id フィールドは挨拶の一意の識別子であり、content は挨拶のテキスト表現です。
挨拶表現をモデル化するために、リソース表現クラスを作成します。以下のリストに示すように、id および content データのフィールド、コンストラクター、アクセサーを含むシンプルなデータ表現(Java ではレコード、Kotlin ではデータクラス)を提供します。
package com.example.restservicecors;
public record Greeting(long id, String content) {
public Greeting() {
this(-1, "");
}
}package com.example.restservicecors
data class Greeting(
val id: Long = -1,
val content: String = ""
)Spring は Jackson JSON (英語) ライブラリを使用して、Greeting 型のインスタンスを JSON に自動的に直列化します。 |
リソースコントローラーを作成する
Spring の RESTful Web サービス構築アプローチでは、HTTP リクエストはコントローラーによって処理されます。これらのコンポーネントは @Controller (Javadoc) アノテーションによって簡単に識別でき、以下のリストに示す GreetingController は /greeting への GET リクエストを処理し、Greeting クラスの新しいインスタンスを返します。
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();
@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));
}
}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
class GreetingController {
private val counter = AtomicLong()
@GetMapping("/greeting")
fun greeting(@RequestParam(required = false, defaultValue = "World") name: String): Greeting {
println("==== get greeting ====")
return Greeting(id = counter.incrementAndGet(), content = "Hello, $name!")
}
}このコントローラーは簡潔でシンプルですが、内部ではさまざまなことが行われています。段階的にそれを分解します。
@RequestMapping アノテーションは、/greeting への HTTP リクエストが greeting() メソッドにマップされることを保証します。
前の例では、@RequestMapping(method = RequestMethod.GET) のショートカットとして機能する @GetMapping アノテーションを使用しています。この場合、テストに便利な GET を使用します。Spring は、オリジンが CORS 構成と一致しない場合でも GET リクエストを拒否します。ブラウザーは CORS プリフライトリクエストを送信する必要はありませんが、プリフライトチェックをトリガーする場合は、@PostMapping を使用して、本文で JSON を受け入れることができます。 |
@RequestParam は、name クエリ文字列パラメーターの値を greeting() メソッドの name パラメーターにバインドします。このクエリ文字列パラメーターは required ではありません。リクエストにない場合、World の defaultValue が使用されます。
メソッド本体の実装は、counter の次の値に基づく id 属性の値と、クエリパラメーターまたはデフォルト値に基づく content の値を使用して、新しい Greeting オブジェクトを作成して返します。また、グリーティング template を使用して、指定された name をフォーマットします。
従来の MVC コントローラーと前述の RESTful Web サービスコントローラーの主な違いは、HTTP レスポンスの本文が作成される方法です。この RESTful Web サービスコントローラーは、ビューテクノロジーに依存して、グリーティングデータを HTML にサーバー側でレンダリングするのではなく、Greeting オブジェクトを生成して返します。オブジェクトデータは、JSON として HTTP レスポンスに直接書き込まれます。
これを実現するために、@RestController (Javadoc) アノテーションは、すべてのメソッドがデフォルトで @ResponseBody (Javadoc) セマンティクスを継承することを前提としています。返されるオブジェクトデータはレスポンスボディに直接挿入されます。
Spring の HTTP メッセージコンバーターのサポートのおかげで、Greeting オブジェクトは自然に JSON に変換されます。Jackson (英語) はクラスパス上にあるため、Spring の MappingJackson2HttpMessageConverter (Javadoc) が自動的に選択され、Greeting インスタンスが JSON に変換されます。
CORS を有効にする
個々のコントローラーまたはグローバルで、クロスオリジンリソース共有(CORS)を有効にできます。次のトピックでは、その方法について説明します。
コントローラーメソッド CORS 設定
RESTful Web サービスのレスポンスに CORS アクセス制御ヘッダーが含まれるようにするには、次のように、ハンドラーメソッドに @CrossOrigin アノテーションを追加する必要があります。
@CrossOrigin(origins = "http://localhost: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(origins = ["http://localhost:9000"])
@GetMapping("/greeting")
fun greeting(@RequestParam(required = false, defaultValue = "World") name: String): Greeting {
println("==== get greeting ====")
return Greeting(id = counter.incrementAndGet(), content = "Hello, $name!")
} この @CrossOrigin アノテーションは、この特定のメソッドに対してのみ、クロスオリジンリソース共有を有効にします。デフォルトでは、すべてのオリジン、すべてのヘッダー、@RequestMapping アノテーションで指定された HTTP メソッドが許可されます。また、30 分の maxAge が使用されます。この動作は、以下のアノテーション属性のいずれかの値を指定することによりカスタマイズできます。
originsoriginPatternsmethodsallowedHeadersexposedHeadersallowCredentialsmaxAge.
この例では、http://localhost:9000 のみがクロスオリジンリクエストの送信を許可しています。
また、コントローラークラスレベルで @CrossOrigin アノテーションを追加して、このクラスのすべてのハンドラーメソッドで CORS を有効にすることもできます。 |
グローバル CORS 設定
きめの細かいアノテーションベースの構成に加えて (または代替として)、いくつかのグローバル CORS 構成も定義できます。これは Filter の使用に似ていますが、Spring MVC 内で宣言して、きめの細かい @CrossOrigin 構成と組み合わせることができます。デフォルトでは、すべてのオリジンと GET、HEAD、POST メソッドが許可されます。
次のリストは、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));
}@GetMapping("/greeting-javaconfig")
fun greetingWithJavaconfig(@RequestParam(required = false, defaultValue = "World") name: String): Greeting {
println("==== in greeting ====")
return Greeting(id = counter.incrementAndGet(), content = "Hello, $name!")
}greetingWithJavaconfig メソッドと greeting メソッド(コントローラーレベルの CORS 構成で使用)の違いは、ルート(/greeting ではなく /greeting-javaconfig)と @CrossOrigin オリジンの存在です。 |
次のリストは、アプリケーションクラスに CORS マッピングを追加する方法を示しています。
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000");
}
};
}@Bean
fun corsConfigurer() = object : WebMvcConfigurer {
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000")
}
} プロパティ(例の allowedOrigins など)を簡単に変更したり、この CORS 構成を特定のパスパターンに適用したりできます。
| グローバルレベルとコントローラーレベルの CORS 構成を組み合わせることができます。 |
アプリケーションクラスの作成
Spring Initializr は、必要最低限のアプリケーションクラスを作成します。以下のリストは、その初期クラスを示しています。
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);
}
}package com.example.restservicecors
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class RestServiceCorsApplication
fun main(args: Array<String>) {
runApplication<RestServiceCorsApplication>(*args)
}クロスオリジンリソース共有の処理方法を設定するためのメソッドを追加する必要があります。以下のリストにその方法を示します。
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000");
}
};
}@Bean
fun corsConfigurer() = object : WebMvcConfigurer {
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost: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("http://localhost:9000");
}
};
}
}package com.example.restservicecors
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import org.springframework.web.servlet.config.annotation.CorsRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
@SpringBootApplication
class RestServiceCorsApplication {
@Bean
fun corsConfigurer() = object : WebMvcConfigurer {
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000")
}
}
}
fun main(args: Array<String>) {
runApplication<RestServiceCorsApplication>(*args)
}@SpringBootApplication は、次のすべてを追加する便利なアノテーションです。
@Configuration: アプリケーションコンテキストの Bean 定義のソースとしてクラスにタグを付けます。@EnableAutoConfiguration: クラスパス設定、他の Bean、さまざまなプロパティ設定に基づいて Bean の追加を開始するよう Spring Boot に指示します。例:spring-webmvcがクラスパスにある場合、このアノテーションはアプリケーションに Web アプリケーションとしてフラグを立て、DispatcherServletのセットアップなどの主要な動作をアクティブにします。@ComponentScan: Spring に、com/exampleパッケージ内の他のコンポーネント、構成、サービスを探して、コントローラーを検出させるように指示します。
main() メソッドは、Spring Boot の SpringApplication.run() メソッドを使用してアプリケーションを起動します。XML が 1 行もないことに気付きましたか? 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 ファイルを作成することもできます。 |
ロギング出力が表示されます。サービスは数秒以内に起動して実行されるはずです。
サービスをテストする
サービスが開始されたため、ブラウザーで http://localhost:8080/greeting にアクセスすると、次のように表示されます。
{"id":1,"content":"Hello, World!"}http://localhost: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 (public/hello.js から) という名前の単純な JavaScript ファイルを作成します。
$(document).ready(function() {
$.ajax({
url: "http://localhost:8080/greeting"
}).then(function(data, status, jqxhr) {
$('.greeting-id').append(data.id);
$('.greeting-content').append(data.content);
console.log(jqxhr);
});
}); このスクリプトは、jQuery を使用して http://localhost:8080/greeting で REST サービスを使用します。次のリスト ( public/index.html から) に示すように、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 の動作をテストするには、クライアントを別のサーバーまたはポートから起動する必要があります。これにより、2 つのアプリケーション間の衝突を回避できるだけでなく、クライアントコードがサービスとは異なるオリジンから提供されるようになります。
ローカルホストのポート 9000 で実行されているクライアントを起動するには、アプリケーションをポート 8080 で実行したままにし、別のターミナルで次の Maven コマンドを実行します。
./mvnw spring-boot:run -Dspring-boot.run.jvmArguments='-Dserver.port=9000'Gradle を使用する場合は、次のコマンドを使用できます。
./gradlew bootRun --args="--server.port=9000"アプリが起動したら、ブラウザーで http://localhost:9000 を開きます。サービスレスポンスには関連する CORS ヘッダーが含まれているため、ID とコンテンツがページにレンダリングされ、次のように表示されます。

ここで、ポート 9000 で実行されているアプリケーションを停止し、ポート 8080 でアプリケーションを実行したまま、別のターミナルで次の Maven コマンドを実行します。
./mvnw spring-boot:run -Dspring-boot.run.jvmArguments='-Dserver.port=9001'Gradle を使用する場合は、次のコマンドを使用できます。
./gradlew bootRun --args="--server.port=9001"アプリが起動したら、ブラウザーで http://localhost:9001 を開きます。ここで、次のように表示されます。

ここでは、http://localhost:9001 ではなく http://localhost:9000 からのクロスオリジンリクエストのみが許可されているため、CORS ヘッダーが欠落している (またはクライアントにとって不十分である) ため、ブラウザーはリクエストに失敗し、値が DOM にレンダリングされません。
要約
おめでとう! Spring とのクロスオリジンリソース共有を含む RESTful Web サービスを開発しました。
関連事項
次のガイドも役立つかもしれません:
新しいガイドを作成したり、既存のガイドに貢献したいですか? 投稿ガイドラインを参照してください [GitHub] (英語) 。
| すべてのガイドは、コード用の ASLv2 ライセンス、およびドキュメント用の帰属、NoDerivatives クリエイティブコモンズライセンス (英語) でリリースされています。 |