このバージョンはまだ開発中であり、まだ安定しているとは見なされていません。最新の安定バージョンについては、Spring Framework 6.2.8 を使用してください! |
リクエストマッピング
このセクションでは、アノテーション付きコントローラーのリクエストマッピングについて説明します。
@RequestMapping
@RequestMapping
アノテーションを使用して、リクエストをコントローラーメソッドにマッピングできます。URL、HTTP メソッド、リクエストパラメーター、ヘッダー、メディア型で一致するさまざまな属性があります。クラスレベルで使用して共有マッピングを表現したり、メソッドレベルで使用して特定のエンドポイントマッピングに絞り込んだりできます。
@RequestMapping
の HTTP メソッド固有のショートカットバリアントもあります。
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
ショートカットはカスタムアノテーションであり、ほとんどのコントローラーメソッドは、デフォルトですべての HTTP メソッドに一致する @RequestMapping
を使用するのではなく、特定の HTTP メソッドにマップする必要があるためです。共有マッピングを表現するには、クラスレベルで @RequestMapping
が引き続き必要です。
@RequestMapping は、同じ要素 (クラス、インターフェース、メソッド) で宣言されている他の @RequestMapping アノテーションと組み合わせて使用することはできません。同じ要素で複数の @RequestMapping アノテーションが検出された場合、警告がログに記録され、最初のマッピングのみが使用されます。これは、@GetMapping 、@PostMapping などの合成 @RequestMapping アノテーションにも当てはまります。 |
次の例には、型およびメソッドレベルのマッピングがあります。
Java
Kotlin
@RestController
@RequestMapping("/persons")
class PersonController {
@GetMapping("/{id}")
public Person getPerson(@PathVariable Long id) {
// ...
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public void add(@RequestBody Person person) {
// ...
}
}
@RestController
@RequestMapping("/persons")
class PersonController {
@GetMapping("/{id}")
fun getPerson(@PathVariable id: Long): Person {
// ...
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
fun add(@RequestBody person: Person) {
// ...
}
}
URI パターン
@RequestMapping
メソッドは、URL パターンを使用してマッピングできます。2 つの選択肢があります。
PathPattern
—PathContainer
としても事前に解析された URL パスと照合される事前に解析されたパターン。このソリューションは Web 使用向けに設計されており、エンコーディングとパスパラメーターを効果的に処理し、効率的に一致します。AntPathMatcher
— 文字列パターンを文字列パスと照合します。これは、Spring 構成でもクラスパス、ファイルシステム、その他の場所のリソースを選択するために使用される元のソリューションです。これは効率が悪く、文字列パスの入力は、URL のエンコーディングやその他の課題を効果的に処理するための課題です。
PathPattern
は、Web アプリケーションに推奨されるソリューションであり、Spring WebFlux で唯一の選択肢です。バージョン 5.3 から Spring MVC での使用が有効になり、バージョン 6.0 からデフォルトで有効になります。パスマッチングオプションのカスタマイズについては、MVC 設定を参照してください。
PathPattern
は、AntPathMatcher
と同じパターン構文をサポートします。さらに、パスの末尾の 0 個以上のパスセグメントに一致するキャプチャーパターン ( {*spring}
など) もサポートします。また、PathPattern
は、複数のパスセグメントに一致する **
の使用をパターンの末尾でのみ許可するように制限します。これにより、特定のリクエストに最適な一致パターンを選択するときに、多くのあいまいさが排除されます。完全なパターン構文については、PathPattern (Javadoc) および AntPathMatcher (Javadoc) を参照してください。
いくつかのパターン例:
"/resources/ima?e.png"
- パスセグメントの 1 文字に一致"/resources/*.png"
- パスセグメントの 0 個以上の文字に一致"/resources/**"
- 複数のパスセグメントに一致"/projects/{project}/versions"
- パスセグメントを一致させ、変数としてキャプチャーする"/projects/{project:[a-z]+}/versions"
- 変数を正規表現に一致させてキャプチャーする
キャプチャーされた URI 変数は @PathVariable
でアクセスできます。例:
Java
Kotlin
@GetMapping("/owners/{ownerId}/pets/{petId}")
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
// ...
}
@GetMapping("/owners/{ownerId}/pets/{petId}")
fun findPet(@PathVariable ownerId: Long, @PathVariable petId: Long): Pet {
// ...
}
次の例に示すように、クラスおよびメソッドレベルで URI 変数を宣言できます。
Java
Kotlin
@Controller
@RequestMapping("/owners/{ownerId}")
public class OwnerController {
@GetMapping("/pets/{petId}")
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
// ...
}
}
@Controller
@RequestMapping("/owners/{ownerId}")
class OwnerController {
@GetMapping("/pets/{petId}")
fun findPet(@PathVariable ownerId: Long, @PathVariable petId: Long): Pet {
// ...
}
}
URI 変数は適切な型に自動的に変換されるか、TypeMismatchException
が発生します。単純型(int
、long
、Date
など)はデフォルトでサポートされており、他のデータ型のサポートを登録できます。型変換および DataBinder
を参照してください。
URI 変数に明示的に名前を付けることができますが (たとえば、@PathVariable("customId")
)、名前が同じで、コードが -parameters
コンパイラーフラグでコンパイルされている場合は、その詳細を省略できます。
構文 {varName:regex}
は、{varName:regex}
の構文を持つ正規表現で URI 変数を宣言します。例: URL "/spring-web-3.0.5.jar"
を指定すると、次のメソッドは名前、バージョン、ファイル拡張子を抽出します。
Java
Kotlin
@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
public void handle(@PathVariable String name, @PathVariable String version, @PathVariable String ext) {
// ...
}
@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
fun handle(@PathVariable name: String, @PathVariable version: String, @PathVariable ext: String) {
// ...
}
URI パスパターンには、PropertySourcesPlaceholderConfigurer
をローカル、システム、環境、その他のプロパティソースに対して使用することにより、起動時に解決される ${…}
プレースホルダーを埋め込むこともできます。これを使用して、たとえば、外部設定に基づいてベース URL をパラメーター化できます。
パターン比較
複数のパターンが URL に一致する場合は、最適なものを選択する必要があります。これは、解析された PathPattern
の使用が有効になっているかどうかに応じて、次のいずれかで実行されます。
どちらも、より具体的なパターンを上位にしてパターンを並べ替えるのに役立ちます。URI 変数の数 (1 としてカウント)、単一のワイルドカード (1 としてカウント)、および二重ワイルドカード (2 としてカウント) の数が少ないほど、パターンはより具体的になります。スコアが等しい場合、より長いパターンが選択されます。同じスコアと長さの場合、ワイルドカードよりも多くの URI 変数を含むパターンが選択されます。
デフォルトのマッピングパターン(/**
)はスコアリングから除外され、常に最後にソートされます。また、プレフィックスパターン(/public/**
など)は、二重ワイルドカードを持たない他のパターンよりも特定性が低いと見なされます。
詳細については、上記のパターンコンパレータへのリンクをたどってください。
サフィックスマッチと RFD
反射ファイルダウンロード(RFD)攻撃は、レスポンスに反映されるリクエスト入力(クエリパラメーターや URI 変数など)に依存するという点で XSS に似ています。ただし、JavaScript を HTML に挿入する代わりに、ブラウザーを切り替えてダウンロードを実行し、後でダブルクリックしたときにレスポンスを実行可能なスクリプトとして処理することに依存します。
Spring MVC では、@ResponseBody
および ResponseEntity
メソッドが危険にさらされます。これは、クライアントが URL パス拡張を介してリクエストできるさまざまなコンテンツ型をレンダリングできるためです。サフィックスパターンマッチングを無効にし、コンテンツネゴシエーションにパス拡張を使用すると、リスクは低下しますが、RFD 攻撃を防ぐには不十分です。
RFD 攻撃を防ぐために、Spring MVC は、レスポンス本文をレンダリングする前に、固定された安全なダウンロードファイルを提案する Content-Disposition:inline;filename=f.txt
ヘッダーを追加します。これは、URL パスに、コンテンツネゴシエーションに対して安全として許可されておらず、明示的に登録されていないファイル拡張子が含まれている場合にのみ行われます。ただし、URL をブラウザーに直接入力すると、副作用が生じる可能性があります。
多くの一般的なパス拡張は、デフォルトで安全として許可されています。カスタム HttpMessageConverter
実装を使用するアプリケーションは、コンテンツネゴシエーションのファイル拡張子を明示的に登録して、それらの拡張子に Content-Disposition
ヘッダーが追加されないようにすることができます。コンテンツタイプを参照してください。
RFD に関連する追加の推奨事項については、CVE-2015-5211 (英語) を参照してください。
消費可能なメディア型
次の例に示すように、リクエストの Content-Type
に基づいてリクエストマッピングを絞り込むことができます。
Java
Kotlin
@PostMapping(path = "/pets", consumes = "application/json") (1)
public void addPet(@RequestBody Pet pet) {
// ...
}
1 | consumes 属性を使用して、コンテンツ型ごとにマッピングを絞り込みます。 |
@PostMapping("/pets", consumes = ["application/json"]) (1)
fun addPet(@RequestBody pet: Pet) {
// ...
}
1 | consumes 属性を使用して、コンテンツ型ごとにマッピングを絞り込みます。 |
consumes
属性は否定表現もサポートします。たとえば、!text/plain
は text/plain
以外のコンテンツ型を意味します。
クラスレベルで共有 consumes
属性を宣言できます。ただし、他のほとんどのリクエストマッピング属性とは異なり、クラスレベルで使用する場合、メソッドレベルの consumes
属性はクラスレベルの宣言を継承するのではなくオーバーライドします。
MediaType は、APPLICATION_JSON_VALUE や APPLICATION_XML_VALUE などの一般的に使用されるメディア型に定数を提供します。 |
生産可能なメディア型
次の例に示すように、Accept
リクエストヘッダーとコントローラーメソッドが生成するコンテンツ型のリストに基づいて、リクエストマッピングを絞り込むことができます。
Java
Kotlin
@GetMapping(path = "/pets/{petId}", produces = "application/json") (1)
@ResponseBody
public Pet getPet(@PathVariable String petId) {
// ...
}
1 | produces 属性を使用して、コンテンツ型ごとにマッピングを絞り込みます。 |
@GetMapping("/pets/{petId}", produces = ["application/json"]) (1)
@ResponseBody
fun getPet(@PathVariable petId: String): Pet {
// ...
}
1 | produces 属性を使用して、コンテンツ型ごとにマッピングを絞り込みます。 |
メディア型は文字セットを指定できます。否定式がサポートされています。たとえば、!text/plain
は、"text/plain" 以外のすべてのコンテンツ型を意味します。
クラスレベルで共有 produces
属性を宣言できます。ただし、他のほとんどのリクエストマッピング属性とは異なり、クラスレベルで使用する場合、メソッドレベルの produces
属性はクラスレベルの宣言を継承するのではなくオーバーライドします。
MediaType は、APPLICATION_JSON_VALUE や APPLICATION_XML_VALUE などの一般的に使用されるメディア型に定数を提供します。 |
パラメーター、ヘッダー
リクエストパラメーターの条件に基づいて、リクエストマッピングを絞り込むことができます。リクエストパラメーターの存在(myParam
)、パラメーターの不在(!myParam
)、特定の値(myParam=myValue
)をテストできます。次の例は、特定の値をテストする方法を示しています。
Java
Kotlin
@GetMapping(path = "/pets/{petId}", params = "myParam=myValue") (1)
public void findPet(@PathVariable String petId) {
// ...
}
1 | myParam が myValue と等しいかどうかのテスト。 |
@GetMapping("/pets/{petId}", params = ["myParam=myValue"]) (1)
fun findPet(@PathVariable petId: String) {
// ...
}
1 | myParam が myValue と等しいかどうかのテスト。 |
次の例に示すように、リクエストヘッダー条件で同じものを使用することもできます。
Java
Kotlin
@GetMapping(path = "/pets/{petId}", headers = "myHeader=myValue") (1)
public void findPet(@PathVariable String petId) {
// ...
}
1 | myHeader が myValue と等しいかどうかのテスト。 |
@GetMapping("/pets/{petId}", headers = ["myHeader=myValue"]) (1)
fun findPet(@PathVariable petId: String) {
// ...
}
1 | myHeader が myValue と等しいかどうかのテスト。 |
API バージョン
API バージョンを指定する標準的な方法はないため、まず MVC 構成とその他の設定オプションを使って API バージョンを設定する必要があります。これにより、リクエストマッピングをサポートする ApiVersionStrategy が作成されます。
API のバージョン管理を有効にすると、リクエストをバージョンにマッピングできるようになります。@RequestMapping
のバージョン属性は以下をサポートします。
値がありません — どのバージョンにも一致
固定バージョン("1.2") — 指定されたバージョンのみに一致します
ベースラインバージョン( "1.2+" ) — 指定されたバージョン以上と一致
複数のコントローラーメソッドのバージョンがリクエストバージョン以下である場合、マッピングの目的でリクエストバージョンに最も近いメソッドが考慮され、実質的に残りのメソッドが置き換えられます。
To illustrate this, consider the following controller mappings:
Java
@RestController
@RequestMapping("/account/{id}")
public class AccountController {
@GetMapping (1)
public Account getAccount() {
}
@GetMapping(version = "1.1") (2)
public Account getAccount1_1() {
}
@GetMapping(version = "1.2+") (3)
public Account getAccount1_2() {
}
@GetMapping(version = "1.5") (4)
public Account getAccount1_5() {
}
}
1 | どのバージョンにも一致 |
2 | match version 1.1 |
3 | match version 1.2 and above |
4 | match version 1.5 |
For request with version "1.3"
:
(1) matches as it matches any version
(2) does not match
(3) matches as it matches 1.2 and above, and is chosen as the highest match
(4) is higher and does not match
For request with version "1.5"
:
(1) matches as it matches any version
(2) does not match
(3) matches as it matches 1.2 and above
(4) matches and is chosen as the highest match
A request with version "1.6"
does not have a match. (1) and (3) do match, but are superseded by (4), which does not match. In this scenario, NotAcceptableApiVersionException
is raised resulting in a 400 response.
The above assumes the request version is a "supported" versions. If not it would fail 検証。 |
HTTP HEAD, OPTIONS
@GetMapping
(および @RequestMapping(method=HttpMethod.GET)
)は、リクエストマッピングのために HTTP HEAD を透過的にサポートします。コントローラーのメソッドを変更する必要はありません。jakarta.servlet.http.HttpServlet
で適用されるレスポンスラッパーは、Content-Length
ヘッダーが(実際にレスポンスに書き込まずに)書き込まれたバイト数に設定されるようにします。
デフォルトでは、HTTP OPTIONS は、Allow
レスポンスヘッダーを、一致する URL パターンを持つすべての @RequestMapping
メソッドにリストされている HTTP メソッドのリストに設定することによって処理されます。
HTTP メソッド宣言のない @RequestMapping
の場合、Allow
ヘッダーは GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS
に設定されます。コントローラーメソッドは、サポートされている HTTP メソッドを常に宣言する必要があります(たとえば、HTTP メソッド固有のバリアント(@GetMapping
、@PostMapping
など)を使用する)。
@RequestMapping
メソッドを明示的に HTTP HEAD および HTTP OPTIONS にマップできますが、これは一般的なケースでは必要ありません。
カスタムアノテーション
Spring MVC は、リクエストマッピングのための構成済みアノテーションの使用をサポートしています。それらは、それ自体が @RequestMapping
でメタアノテーションが付けられたアノテーションであり、@RequestMapping
属性のサブセット (またはすべて) をより狭く、より具体的な目的で再宣言するように構成されています。
@GetMapping
、@PostMapping
、@PutMapping
、@DeleteMapping
、@PatchMapping
は、合成されたアノテーションの例です。これらが提供されるのは、おそらく、デフォルトですべての HTTP メソッドに一致する @RequestMapping
を使用するのではなく、ほとんどのコントローラーメソッドを特定の HTTP メソッドにマップする必要があるためです。合成されたアノテーションを実装する方法の例が必要な場合は、それらがどのように宣言されているかを確認してください。
@RequestMapping は、同じ要素 (クラス、インターフェース、メソッド) で宣言されている他の @RequestMapping アノテーションと組み合わせて使用することはできません。同じ要素で複数の @RequestMapping アノテーションが検出された場合、警告がログに記録され、最初のマッピングのみが使用されます。これは、@GetMapping 、@PostMapping などの合成 @RequestMapping アノテーションにも当てはまります。 |
Spring MVC は、カスタムリクエストマッチングロジックを使用したカスタムリクエストマッピング属性もサポートしています。これは、RequestMappingHandlerMapping
のサブクラス化と getCustomMethodCondition
メソッドのオーバーライドを必要とするより高度なオプションであり、カスタム属性をチェックして独自の RequestCondition
を返すことができます。
明示的な登録
ハンドラーメソッドをプログラムで登録できます。これは、動的な登録や、異なる URL での同じハンドラーの異なるインスタンスなどの高度なケースに使用できます。次の例では、ハンドラーメソッドを登録します。
Java
Kotlin
@Configuration
public class MyConfig {
@Autowired
public void setHandlerMapping(RequestMappingHandlerMapping mapping, UserHandler handler) (1)
throws NoSuchMethodException {
RequestMappingInfo info = RequestMappingInfo
.paths("/user/{id}").methods(RequestMethod.GET).build(); (2)
Method method = UserHandler.class.getMethod("getUser", Long.class); (3)
mapping.registerMapping(info, handler, method); (4)
}
}
1 | ターゲットハンドラーとコントローラーのハンドラーマッピングを挿入します。 |
2 | リクエストマッピングメタデータを準備します。 |
3 | ハンドラーメソッドを取得します。 |
4 | 登録を追加します。 |
@Configuration
class MyConfig {
@Autowired
fun setHandlerMapping(mapping: RequestMappingHandlerMapping, handler: UserHandler) { (1)
val info = RequestMappingInfo.paths("/user/{id}").methods(RequestMethod.GET).build() (2)
val method = UserHandler::class.java.getMethod("getUser", Long::class.java) (3)
mapping.registerMapping(info, handler, method) (4)
}
}
1 | ターゲットハンドラーとコントローラーのハンドラーマッピングを挿入します。 |
2 | リクエストマッピングメタデータを準備します。 |
3 | ハンドラーメソッドを取得します。 |
4 | 登録を追加します。 |
@HttpExchange
@HttpExchange
の主な目的は、生成されたプロキシを使用して HTTP クライアントコードを抽象化することですが、そのようなアノテーションが配置される HTTP インターフェースは、クライアントとサーバーの使用に中立的な契約です。クライアントコードを簡素化することに加えて、HTTP インターフェースはサーバーがクライアントアクセス用に API を公開する便利なメソッドである場合もあります。これはクライアントとサーバー間の結合の増加につながり、特にパブリック API にとっては良い選択ではないことがよくありますが、内部 API にとってはまさにゴールとなる可能性があります。これは Spring Cloud で一般的に使用されるアプローチであり、コントローラークラスでのサーバー側の処理のために @RequestMapping
の代替として @HttpExchange
がサポートされるのはこのためです。
例:
Java
Kotlin
@HttpExchange("/persons")
interface PersonService {
@GetExchange("/{id}")
Person getPerson(@PathVariable Long id);
@PostExchange
void add(@RequestBody Person person);
}
@RestController
class PersonController implements PersonService {
public Person getPerson(@PathVariable Long id) {
// ...
}
@ResponseStatus(HttpStatus.CREATED)
public void add(@RequestBody Person person) {
// ...
}
}
@HttpExchange("/persons")
interface PersonService {
@GetExchange("/{id}")
fun getPerson(@PathVariable id: Long): Person
@PostExchange
fun add(@RequestBody person: Person)
}
@RestController
class PersonController : PersonService {
override fun getPerson(@PathVariable id: Long): Person {
// ...
}
@ResponseStatus(HttpStatus.CREATED)
override fun add(@RequestBody person: Person) {
// ...
}
}
@HttpExchange
と @RequestMapping
には違いがあります。@RequestMapping
はパスパターン、HTTP メソッドなどによって任意の数のリクエストにマップできますが、@HttpExchange
は具体的な HTTP メソッド、パス、コンテンツ型を使用して単一のエンドポイントを宣言します。
メソッドパラメーターと戻り値については、通常、@HttpExchange
は @RequestMapping
が行うメソッドパラメーターのサブセットをサポートします。特に、サーバー側固有のパラメーター型は除外されます。詳細は @HttpExchange、@RequestMapping の一覧を参照してください。
@HttpExchange
は、クライアント側の @RequestMapping(headers={})
と同様に "name=value"
-like ペアを受け入れる headers()
パラメーターもサポートします。サーバー側では、これは @RequestMapping
がサポートする完全な構文に拡張されます。