{"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=User
name
パラメーター値は、次のように、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.git
gs-rest-service-cors/initial
に cdリソース表現クラスを作成するにジャンプしてください。
完了したときは、gs-rest-service-cors/complete
のコードに対して結果を確認できます。
Spring Initializr から開始
IDE を使用する場合はプロジェクト作成ウィザードを使用します。IDE を使用せずにコマンドラインなどで開発する場合は、この事前に初期化されたプロジェクトからプロジェクトを ZIP ファイルとしてダウンロードできます。このプロジェクトは、このチュートリアルの例に合うように構成されています。
プロジェクトを手動で初期化するには:
IDE のメニューまたはブラウザーから Spring Initializr を開きます。アプリケーションに必要なすべての依存関係を取り込み、ほとんどのセットアップを行います。
Gradle または Maven のいずれかと、使用する言語を選択します。このガイドは、Java を選択したことを前提としています。
依存関係をクリックして、Spring Web を選択します。
生成をクリックします。
結果の ZIP ファイルをダウンロードします。これは、選択して構成された Web アプリケーションのアーカイブです。
Eclipse や IntelliJ のような IDE は新規プロジェクト作成ウィザードから Spring Initializr の機能が使用できるため、手動での ZIP ファイルのダウンロードやインポートは不要です。 |
プロジェクトを Github からフォークして、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 サービスを作成できます。
サービスの相互作用について考えることでプロセスを開始します。
サービスは、/greeting
への GET
リクエストを処理します。オプションで、クエリ文字列に name
パラメーターを使用します。GET
リクエストは、あいさつを表すために本文に JSON を含む 200 OK
レスポンスを返す必要があります。次のようになります。
{
"id": 1,
"content": "Hello, World!"
}
id
フィールドは挨拶の一意の識別子であり、content
は挨拶のテキスト表現です。
挨拶表現をモデル化するには、リソース表現クラスを作成します。次のリスト(src/main/java/com/example/restservicecors/Greeting.java
から)が示すように、id
および content
データのフィールド、コンストラクター、アクセサーを持つプレーンな古い 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 に自動的にマーシャリングします。 |
リソースコントローラーを作成する
RESTful Web サービスを構築する Spring のアプローチでは、HTTP リクエストはコントローラーによって処理されます。これらのコンポーネントは @Controller
(Javadoc) アノテーションによって簡単に識別され、次のリスト(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 = "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));
}
}
このコントローラーは簡潔でシンプルですが、内部ではさまざまなことが行われています。段階的にそれを分解します。
@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 アクセス制御ヘッダーが含まれるように、次のリスト(src/main/java/com/example/restservicecors/GreetingController.java
から)が示すように、ハンドラーメソッドに @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
アノテーションは、この特定のメソッドでのみクロスオリジンリソース共有を有効にします。デフォルトでは、@RequestMapping
アノテーションで指定されたすべてのオリジン、すべてのヘッダー、HTTP メソッドを許可します。また、30 分の maxAge
が使用されます。次のいずれかのアノテーション属性の値を指定することにより、この動作をカスタマイズできます。
origins
originPatterns
methods
allowedHeaders
exposedHeaders
allowCredentials
maxAge
.
この例では、http://localhost: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 ではなく /greeting-javaconfig )と @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("http://localhost: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("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");
}
};
}
}
@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
(complete/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 サービスを使用します。次のリスト ( complete/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 ライセンス、およびドキュメント用の Attribution、NoDerivatives creative commons ライセンス (英語) でリリースされています。 |