Spring MVC/Boot @RequestMapping
アノテーション徹底解説
はじめに
Webアプリケーション開発において、特定のリクエストを処理するコード(ハンドラー)にマッピングすることは、フレームワークの最も基本的な機能の一つです。Spring Framework、特にSpring MVCおよびその進化形であるSpring Bootでは、この重要な役割を主に@RequestMapping
アノテーションが担っています。
@RequestMapping
アノテーションは非常に強力で柔軟性が高く、単にURLとメソッドを紐づけるだけでなく、HTTPメソッド、リクエストパラメータ、ヘッダー、さらにはリクエストやレスポンスのメディアタイプに至るまで、様々な条件に基づいてリクエストを適切なハンドラーメソッドに振り分けることができます。
この記事では、Spring MVCおよびSpring Bootにおける@RequestMapping
アノテーションについて、その基本的な使い方から高度な機能、実践的なヒントまでを徹底的に解説します。約5000語にわたる詳細な説明と豊富なコード例を通して、読者が@RequestMapping
アノテーションを完全に理解し、自身のアプリケーション開発で自信を持って使いこなせるようになることを目指します。
この記事を読むことで、あなたは以下のことを習得できます。
@RequestMapping
の基本的な役割と、クラスレベルおよびメソッドレベルでの適用方法- HTTPメソッドに基づいたリクエストマッピングの方法
- URLパスから動的に値を取り出すパス変数の使い方
- クエリパラメータやフォームデータを受け取る方法
- リクエストヘッダーやCookieの値を利用したマッピングやデータ取得
- リクエストやレスポンスのメディアタイプによる条件付け
- パラメータやヘッダーの有無・値による詳細なマッピング制御
- パスパターン(ワイルドカード)を使った柔軟なマッピング
- SpringBoot環境における
@RequestMapping
の typical な利用方法 - 効率的で保守性の高いAPI設計における
@RequestMapping
の活用法
さあ、Springにおけるリクエストマッピングの中核である@RequestMapping
の世界へ深く潜り込んでいきましょう。
@RequestMapping
アノテーションの基本
@RequestMapping
アノテーションは、Spring MVCまたはSpring WebFluxアプリケーションにおいて、HTTPリクエストをコントローラー内の特定のハンドラーメソッドにマッピングするために使用されます。これは、特定のエンドポイント(URL)へのリクエストが来たときに、どのJavaメソッドがそのリクエストを処理すべきかをSpringフレームワークに指示する役割を果たします。
@RequestMapping
は、クラスレベルとメソッドレベルの両方に適用できます。
- クラスレベル: クラス全体に対するベースパスを指定します。これにより、そのクラス内のすべてのハンドラーメソッドのマッピングパスに、指定したベースパスがプレフィックスとして付加されます。
- メソッドレベル: 特定のハンドラーメソッドに対するパスやその他のマッピング条件を指定します。クラスレベルに
@RequestMapping
が指定されている場合、メソッドレベルのパスはクラスレベルのパスからの相対パスとして扱われます。
最も基本的な使い方は、value
属性(またはそのエイリアスであるpath
属性)でリクエストパスを指定することです。
“`java
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller // このクラスがSpring MVCのコントローラーであることを示す
@RequestMapping(“/greeting”) // クラスレベル: このクラス内のメソッドはすべて “/greeting” から始まるパスになる
public class GreetingController {
// メソッドレベル: GETリクエストかつパスが "/greeting" (クラスレベル + メソッドレベル) の場合にこのメソッドが呼ばれる
@RequestMapping("/")
@ResponseBody // メソッドの戻り値をHTTPレスポンスボディに直接書き込む
public String sayHello() {
return "Hello, World!";
}
// メソッドレベル: GETリクエストかつパスが "/greeting/bye" の場合にこのメソッドが呼ばれる
@RequestMapping("/bye")
@ResponseBody
public String sayGoodbye() {
return "Goodbye!";
}
// 複数のパスを指定することも可能
@RequestMapping({"/hi", "/hello"})
@ResponseBody
public String sayHiOrHello() {
return "Hi or Hello!";
}
}
“`
上記の例では、
* GET /greeting
へのリクエストは sayHello()
メソッドによって処理されます。
* GET /greeting/bye
へのリクエストは sayGoodbye()
メソッドによって処理されます。
* GET /greeting/hi
または GET /greeting/hello
へのリクエストは sayHiOrHello()
メソッドによって処理されます。
value
属性とpath
属性は全く同じ意味で、どちらを使っても構いません。通常はより短く直感的なvalue
がよく使われますが、パスであることを強調したい場合はpath
を使う開発者もいます。
また、上記の例ではHTTPメソッドを指定していませんが、@RequestMapping
はデフォルトですべてのHTTPメソッド(GET, POST, PUT, DELETEなど)を受け付けます。特定のリクエストメソッドに限定したい場合は、次に説明するmethod
属性を使用します。
@ResponseBody
アノテーションは、このメソッドの戻り値がViewの名前ではなく、HTTPレスポンスボディに直接書き込まれるべきであることをSpringに指示します。RESTful APIを開発する際によく使われます。クラスレベルに@RestController
アノテーションを使用すると、そのクラス内のすべてのメソッドがデフォルトで@ResponseBody
の動作をするようになります。@RestController
は@Controller
と@ResponseBody
を組み合わせたメタアノテーションです。
リクエストメソッドによるマッピング
Webアプリケーションにおけるリクエストは、GET, POST, PUT, DELETEなどの様々なHTTPメソッドを持ちます。RESTful APIの設計において、これらのメソッドを適切に使い分けることは非常に重要です。@RequestMapping
アノテーションは、method
属性を使って特定のリクエストメソッドにマッピングを限定することができます。
method
属性には、org.springframework.web.bind.annotation.RequestMethod
列挙型の値を指定します。
“`java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.*;
@RestController // @Controller + @ResponseBody
@RequestMapping(“/products”)
public class ProductController {
// GET /products - 全ての製品を取得
@RequestMapping(method = RequestMethod.GET)
public String getAllProducts() {
return "List of all products";
}
// POST /products - 新しい製品を作成
@RequestMapping(method = RequestMethod.POST)
public String createProduct(@RequestBody String productDetails) {
System.out.println("Creating product with details: " + productDetails);
return "Product created";
}
// GET /products/{id} - 特定の製品を取得 (パス変数については後述)
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String getProductById(@PathVariable Long id) {
return "Product with id: " + id;
}
// PUT /products/{id} - 特定の製品を更新
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public String updateProduct(@PathVariable Long id, @RequestBody String productDetails) {
System.out.println("Updating product " + id + " with details: " + productDetails);
return "Product updated";
}
// DELETE /products/{id} - 特定の製品を削除
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public String deleteProduct(@PathVariable Long id) {
System.out.println("Deleting product with id: " + id);
return "Product deleted";
}
}
“`
この例では、/products
という同じパスに対して、HTTPメソッドに応じて異なるメソッドが呼び出されるように設定しています。GET /products
は getAllProducts()
に、POST /products
は createProduct()
にマッピングされます。同様に、/products/{id}
というパス変数を含むパスに対しても、GET, PUT, DELETE メソッドでそれぞれ別のハンドラーメソッドが対応します。
複数のHTTPメソッドに対応させたい場合は、method
属性に配列で指定します。
java
@RequestMapping(value = "/items/{id}", method = {RequestMethod.GET, RequestMethod.HEAD})
public String getItem(@PathVariable Long id) {
// HEADリクエストの場合もこのメソッドが呼ばれますが、Springはボディを含めずにレスポンスを返します
return "Item details for id: " + id;
}
ショートカットアノテーション
Spring 4.3以降では、一般的なHTTPメソッドに対応する専用のショートカットアノテーションが導入されました。これらは@RequestMapping(method = RequestMethod.XXX)
の代わりに使うことができ、コードの可読性を大きく向上させます。
@GetMapping
(GET)@PostMapping
(POST)@PutMapping
(PUT)@DeleteMapping
(DELETE)@PatchMapping
(PATCH)
これらのアノテーションは@RequestMapping
のメタアノテーションであり、内部的には対応するmethod
属性が設定された@RequestMapping
として機能します。例えば、@GetMapping
は@RequestMapping(method = RequestMethod.GET)
と同等です。
ショートカットアノテーションの利用を強く推奨します。
上記のProductController
の例をショートカットアノテーションに書き換えると、より簡潔で分かりやすくなります。
“`java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController // @Controller + @ResponseBody
@RequestMapping(“/api/products”) // クラスレベルのベースパス
public class ProductController {
// GET /api/products - 全ての製品を取得
@GetMapping
public List<Product> getAllProducts() {
// 製品リストを取得するロジック
return List.of(new Product(1L, "Laptop"), new Product(2L, "Mouse"));
}
// POST /api/products - 新しい製品を作成
@PostMapping
public Product createProduct(@RequestBody Product product) {
// 製品を作成するロジック
System.out.println("Creating product: " + product);
return product; // 作成された製品を返す(ここでは受け取ったものをそのまま返す例)
}
// GET /api/products/{id} - 特定の製品を取得
@GetMapping("/{id}") // パス変数 {id} を指定
public Product getProductById(@PathVariable Long id) {
// 指定されたIDの製品を取得するロジック
System.out.println("Fetching product with id: " + id);
return new Product(id, "Sample Product"); // 仮の製品を返す
}
// PUT /api/products/{id} - 特定の製品を更新
@PutMapping("/{id}")
public Product updateProduct(@PathVariable Long id, @RequestBody Product productDetails) {
// 指定されたIDの製品を更新するロジック
System.out.println("Updating product " + id + " with details: " + productDetails);
return new Product(id, productDetails.getName() + " (Updated)"); // 更新された製品を返す
}
// DELETE /api/products/{id} - 特定の製品を削除
@DeleteMapping("/{id}")
public String deleteProduct(@PathVariable Long id) {
// 指定されたIDの製品を削除するロジック
System.out.println("Deleting product with id: " + id);
return "Product with id " + id + " deleted successfully";
}
}
// 簡単なProductクラスの定義 (ここではネストして定義)
class Product {
private Long id;
private String name;
public Product(Long id, String name) {
this.id = id;
this.name = name;
}
// Getter, Setter, toString など (省略)
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@Override
public String toString() { return "Product{id=" + id + ", name='" + name + "'}"; }
}
“`
このように、ショートカットアノテーションを使うことで、どのHTTPメソッドに対応するメソッドなのかが一目で分かり、コードがより読みやすくなります。特別な理由がない限り、ショートカットアノテーションを使用することが推奨されます。
パス変数 (@PathVariable
)
RESTfulなURI設計では、リソースの識別子をURLパスの一部に含めることがよくあります。例えば、/users/123
の 123
はユーザーIDを表すといった場合です。Spring MVCでは、@PathVariable
アノテーションを使用して、URLパスのテンプレート変数から値を取得し、ハンドラーメソッドのパラメータとして受け取ることができます。
パス変数は、@RequestMapping
のvalue
またはpath
属性で {変数名}
の形式で定義します。そして、ハンドラーメソッドのパラメータに@PathVariable
アノテーションを付け、その後にパス変数と同じ名前のパラメータを定義します。
“`java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(“/users”)
public class UserController {
// GET /users/{userId} という形式のリクエストを処理
@GetMapping("/{userId}")
public String getUserProfile(@PathVariable Long userId) {
// URLパスの {userId} の値が Long 型の userId パラメータに自動的にバインドされる
return "User profile for ID: " + userId;
}
// 複数のパス変数も可能
// GET /users/{userId}/orders/{orderId} という形式のリクエストを処理
@GetMapping("/{userId}/orders/{orderId}")
public String getUserOrder(@PathVariable("userId") Long userIdentifier, // パラメータ名とパス変数名が異なる場合は明示的に指定
@PathVariable Long orderId) { // パラメータ名とパス変数名が同じ場合は省略可能
return "Order " + orderId + " for user " + userIdentifier;
}
}
“`
パス変数名の指定
@PathVariable
アノテーションは、デフォルトではメソッドパラメータ名をパス変数名として使用しようとします。上記のgetUserProfile
メソッドのように、メソッドパラメータ名(userId
)とパス変数名({userId}
)が同じであれば、@PathVariable
に名前を指定する必要はありません。
しかし、getUserOrder
メソッドの例のように、メソッドパラメータ名(userIdentifier
)とパス変数名({userId}
)が異なる場合は、@PathVariable("パス変数名")
のように明示的にパス変数名を指定する必要があります。コードの可読性を高めるために、パラメータ名とパス変数名を一致させるか、明示的に名前を指定することが推奨されます。
型変換
Springは、パス変数から取得した文字列をハンドラーメソッドのパラメータの型(プリミティブ型、ラッパー型、Stringなど)に自動的に変換しようとします。上記の例では、{userId}
と{orderId}
の値は文字列として取得されますが、SpringによってLong
型に変換されます。変換に失敗した場合(例えば、数値としてパースできない文字列が渡された場合)、Springはエラーを発生させ、クライアントには通常400 Bad Requestなどのステータスコードが返されます。
カスタム型への変換が必要な場合は、SpringのConverterやFormatter SPIを利用して独自の変換ロジックを定義することも可能です。
パス変数の必須・オプション
デフォルトでは、@PathVariable
は必須です。つまり、パス変数に対応するパスセグメントがリクエストURLに存在しない場合、マッピングに失敗します。
@PathVariable
にはrequired
属性がありますが、これは通常false
に設定することはできません。なぜなら、パス変数はURLパスの一部であり、存在しないとパス自体がマッチしなくなるためです。
補足: @PathVariable
のrequired=false
は技術的には存在しますが、特定のパスパターン(例: /users/{id}
vs /users
)で同じメソッドをマッピングしたい場合にのみ意味を持ちます。しかし、これは混乱を招きやすいため、通常は異なるパスパターンには異なるメソッドを定義するか、@RequestParam
など他の手段を検討すべきです。
正規表現によるパス変数
パス変数には正規表現を適用して、パスセグメントとして許容するパターンをより厳密に制御することができます。これは、ファイル名のようにドット (.
) を含むパスセグメントを扱う際などに特に役立ちます。
パス変数の定義の後に :正規表現
の形式で正規表現を指定します。
“`java
package com.example.demo.controller;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@RestController
@RequestMapping(“/files”)
public class FileController {
// GET /files/my_document.pdf などのリクエストを処理
// パス変数名 {fileName} に続く :.+ は、1文字以上の任意の文字にマッチする正規表現
// これにより、ファイル名にドットが含まれていてもパス変数として正しくキャプチャされる
@GetMapping("/{fileName:.+}")
public ResponseEntity<Resource> downloadFile(@PathVariable String fileName) throws IOException {
System.out.println("Attempting to download file: " + fileName);
// ここではクラスパスからファイルを取得する例
Resource resource = new ClassPathResource("static/" + fileName);
if (resource.exists() && resource.isReadable()) {
MediaType contentType = MediaType.TEXT_PLAIN; // デフォルトのContent-Type
// ファイル名に応じて適切なContent-Typeを設定するロジック (ここでは省略)
if (fileName.endsWith(".pdf")) {
contentType = MediaType.APPLICATION_PDF;
} else if (fileName.endsWith(".png")) {
contentType = MediaType.IMAGE_PNG;
}
// ... 他のファイルタイプ ...
return ResponseEntity.ok()
.contentType(contentType)
.body(resource);
} else {
// ファイルが見つからない場合は404を返す
return ResponseEntity.notFound().build();
}
}
}
“`
通常のパス変数 {fileName}
のままでは、Springのデフォルトのパスマッチング設定によっては、パスセグメント内のドットより後の部分が無視されてしまうことがあります(例: /files/my_document.pdf
が {fileName}
としてmy_document
とマッチするなど)。正規表現を使用することで、この挙動を制御し、my_document.pdf
全体をfileName
として取得できるようになります。
正規表現は強力ですが、複雑になりすぎると可読性を損なう可能性があるため、必要な範囲で使用することが望ましいです。
リクエストパラメータ (@RequestParam
)
HTTPリクエストでは、クエリパラメータ (?key1=value1&key2=value2...
) やフォームデータ (application/x-www-form-urlencoded
や multipart/form-data
) を通じて、追加のデータを送信することがよくあります。Spring MVCでは、@RequestParam
アノテーションを使用して、これらのリクエストパラメータの値を取得し、ハンドラーメソッドのパラメータとして受け取ることができます。
@RequestParam
は、ハンドラーメソッドのパラメータに付与します。デフォルトでは、メソッドパラメータ名と同じ名前のリクエストパラメータを探します。
“`java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping(“/search”)
public class SearchController {
// GET /search?query=spring を処理
@GetMapping
public String performSearch(@RequestParam String query) { // パラメータ名とリクエストパラメータ名が同じ
return "Searching for: " + query;
}
// GET /search/products?name=laptop&category=electronics&page=1&size=10 を処理
@GetMapping("/products")
public String searchProducts(@RequestParam String name,
@RequestParam("category") String productCategory, // パラメータ名とリクエストパラメータ名が異なる場合
@RequestParam int page, // プリミティブ型への変換
@RequestParam Integer size) { // ラッパー型への変換
return String.format("Searching products: name=%s, category=%s, page=%d, size=%d",
name, productCategory, page, size);
}
}
“`
リクエストパラメータ名の指定
@RequestParam
アノテーションも、value
属性(またはname
属性)を使って、対応するリクエストパラメータ名を明示的に指定できます。上記のsearchProducts
メソッドの例のように、@RequestParam("category") String productCategory
は、リクエストパラメータ名category
の値をString
型のproductCategory
パラメータにバインドします。メソッドパラメータ名とリクエストパラメータ名が同じであれば、名前の指定は省略可能です (@RequestParam String name
)。
型変換
@RequestParam
で受け取る値も、Springによって自動的にハンドラーメソッドパラメータの型に変換されます。文字列から数値型 (int
, Integer
, long
, Long
など)、真偽値 (boolean
, Boolean
) など、標準的な型変換は自動で行われます。変換に失敗した場合、通常は400 Bad Requestエラーが返されます。
必須・オプション設定 (required
)
デフォルトでは、@RequestParam
は必須です。つまり、対応するリクエストパラメータがURLまたはフォームデータに含まれていない場合、Springはエラーを発生させます。
パラメータをオプションにしたい場合は、required
属性をfalse
に設定します。
java
// GET /items?id=123 または GET /items の両方を処理したい場合
@GetMapping("/items")
public String getItem(@RequestParam(required = false) Long id) {
if (id != null) {
return "Getting item with ID: " + id;
} else {
return "Getting all items";
}
}
オプションのパラメータを受け取る場合、パラメータの型をラッパー型(Integer
, Long
, Boolean
など)またはOptional<>
にすることをお勧めします。プリミティブ型(int
, long
, boolean
など)はnull
を表現できないため、オプションのパラメータには適していません。required = false
でプリミティブ型を指定した場合、Springはパラメータが欠落している場合にその型のデフォルト値(例: int
なら0
、boolean
ならfalse
)をバインドしようとしますが、これは意図しない挙動を引き起こす可能性があります。
Optional
クラス(Java 8以降)を使うと、パラメータが存在するかどうかをより安全かつ明示的に扱えます。
java
// GET /items?id=123 または GET /items の両方を処理したい場合
@GetMapping("/items_optional")
public String getItemOptional(@RequestParam Optional<Long> id) {
if (id.isPresent()) {
return "Getting item with ID: " + id.get();
} else {
return "Getting all items (optional id)";
}
}
デフォルト値 (defaultValue
)
リクエストパラメータが提供されなかった場合にデフォルト値を設定したい場合は、defaultValue
属性を使用します。defaultValue
属性を指定すると、そのパラメータは自動的にオプション (required = false
相当) になります。
defaultValue
属性の値は文字列として指定し、Springがそれをパラメータの型に変換します。
java
// GET /list?page=2&size=50 または GET /list (page=0, size=20を使用) を処理
@GetMapping("/list")
public String getList(@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
return String.format("Fetching list: page=%d, size=%d", page, size);
}
この例では、リクエストパラメータpage
が指定されなければ0
が、size
が指定されなければ20
が使用されます。
複数の値を持つパラメータ (List
や Array
)
同じ名前のリクエストパラメータが複数存在する場合があります。例えば、GET /products?id=101&id=102&id=103
のように、複数の製品IDを渡したい場合です。Spring MVCでは、ハンドラーメソッドのパラメータをList
や配列として宣言することで、これらの複数の値を簡単に受け取ることができます。
“`java
// GET /products?id=101&id=102&id=103 を処理
@GetMapping(“/products/by-ids”)
public String getProductsByIds(@RequestParam List
return “Fetching products with IDs: ” + id; // 例: “Fetching products with IDs: [101, 102, 103]”
}
// 配列でも同様に受け取れる
@GetMapping(“/products/by-ids-array”)
public String getProductsByIdsArray(@RequestParam Long[] id) {
return “Fetching products with IDs: ” + List.of(id);
}
“`
Springは、同じ名前のパラメータ値を収集し、指定されたList
または配列の型に変換してバインドします。
Mapでのパラメータ取得
全てのリクエストパラメータをキー・バリューペアのMap
として取得したい場合は、@RequestParam Map<String, String> params
のように宣言できます。
java
@GetMapping("/params")
public String getAllParams(@RequestParam Map<String, String> params) {
StringBuilder sb = new StringBuilder("Received parameters:\n");
params.forEach((key, value) -> sb.append(key).append("=").append(value).append("\n"));
return sb.toString();
}
この場合、リクエストパラメータのキーと値はすべて文字列としてMapに格納されます。個別の型変換が必要な場合は、@RequestParam
で個別にパラメータを受け取る方が便利です。
リクエストヘッダー (@RequestHeader
)
HTTPリクエストには、User-Agent
、Accept
、Content-Type
などのヘッダーが含まれます。これらのヘッダー情報は、クライアントの種類、許容するメディアタイプ、リクエストボディの形式などを知るために重要です。Spring MVCでは、@RequestHeader
アノテーションを使用して、特定のリクエストヘッダーの値を取得し、ハンドラーメソッドのパラメータとして受け取ることができます。
@RequestHeader
アノテーションは、ハンドラーメソッドのパラメータに付与します。デフォルトでは、メソッドパラメータ名と同じ名前(ヘッダー名は大文字・小文字を区別しないため、一般的にはケバブケースをキャメルケースに変換した名前)のヘッダーを探します。より確実には、ヘッダー名を明示的に指定します。
“`java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(“/headers”)
public class HeaderController {
// GET /headers - User-Agentヘッダーを取得
@GetMapping
public String getUserAgent(@RequestHeader("User-Agent") String userAgent) { // ヘッダー名を明示的に指定
return "User Agent: " + userAgent;
}
// GET /headers/accept - Acceptヘッダーを取得 (パラメータ名と同じヘッダー名を使用)
@GetMapping("/accept")
public String getAcceptHeader(@RequestHeader String accept) { // "accept" ヘッダーを探す
return "Accept Header: " + accept;
}
// 複数のヘッダーを取得
@GetMapping("/multi")
public String getMultipleHeaders(@RequestHeader("Host") String host,
@RequestHeader("Connection") String connection) {
return String.format("Host: %s, Connection: %s", host, connection);
}
}
“`
ヘッダー名の指定
@RequestHeader
も、value
属性(またはname
属性)で対応するヘッダー名を明示的に指定できます。HTTPヘッダー名は通常、User-Agent
のように単語をハイフンでつなぐケバブケースですが、Javaのパラメータ名としてはキャメルケース(例: userAgent
)が一般的です。そのため、@RequestHeader("User-Agent") String userAgent
のように明示的にヘッダー名を指定することが一般的です。パラメータ名がヘッダー名をキャメルケースに変換したものと一致する場合は、@RequestHeader String userAgent
のように省略してもSpringは正しく解決しようとしますが、明示的に指定する方が安全で分かりやすいでしょう。
必須・オプション設定 (required
) と デフォルト値 (defaultValue
)
@RequestHeader
もrequired
属性とdefaultValue
属性を持ちます。デフォルトではrequired = true
(必須)です。
ヘッダーが存在しない場合にエラーとせず、オプションとして扱いたい場合はrequired = false
を設定します。
java
// Refererヘッダーはオプション
@GetMapping("/referer")
public String getReferer(@RequestHeader(value = "Referer", required = false) String referer) {
if (referer != null) {
return "Referer: " + referer;
} else {
return "No Referer header";
}
}
パラメータが提供されなかった場合にデフォルト値を設定したい場合は、defaultValue
属性を使用します。defaultValue
を指定すると、パラメータは自動的にオプション (required = false
相当) になります。
java
// Custom-Headerが指定されなければデフォルト値を使用
@GetMapping("/custom-header")
public String getCustomHeader(@RequestHeader(value = "Custom-Header", defaultValue = "DefaultValue") String customHeader) {
return "Custom Header: " + customHeader;
}
Mapでのヘッダー取得
全てのリクエストヘッダーをキー・バリューペアのMap
として取得したい場合は、@RequestHeader Map<String, String> headers
のように宣言できます。キーはヘッダー名(通常はオリジナルの形式)、値はヘッダー値の文字列になります。
java
@GetMapping("/all-headers")
public String getAllHeaders(@RequestHeader Map<String, String> headers) {
StringBuilder sb = new StringBuilder("Received headers:\n");
headers.forEach((key, value) -> sb.append(key).append(": ").append(value).append("\n"));
return sb.toString();
}
Cookie (@CookieValue
)
Webアプリケーションでは、セッション管理やユーザーのカスタマイズのためにCookieがよく利用されます。Spring MVCでは、@CookieValue
アノテーションを使用して、特定のリクエストCookieの値を取得し、ハンドラーメソッドのパラメータとして受け取ることができます。
@CookieValue
アノテーションは、ハンドラーメソッドのパラメータに付与します。@RequestParam
や@RequestHeader
と同様に、デフォルトではメソッドパラメータ名と同じ名前のCookieを探しますが、Cookie名を明示的に指定するのが一般的です。
“`java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(“/cookies”)
public class CookieController {
// GET /cookies - MyCookieという名前のCookieの値を取得
@GetMapping
public String getMyCookie(@CookieValue("MyCookie") String myCookieValue) { // Cookie名を明示的に指定
return "Value of MyCookie: " + myCookieValue;
}
// オプションのCookie - JSESSIONIDは存在しない場合もある
@GetMapping("/session")
public String getSessionId(@CookieValue(value = "JSESSIONID", required = false) String sessionId) {
if (sessionId != null) {
return "JSESSIONID: " + sessionId;
} else {
return "JSESSIONID not found";
}
}
// デフォルト値を持つCookie
@GetMapping("/user-pref")
public String getUserPreference(@CookieValue(value = "UserPreference", defaultValue = "default") String userPreference) {
return "User Preference: " + userPreference;
}
}
“`
Cookie名の指定
@CookieValue
も、value
属性(またはname
属性)で対応するCookie名を明示的に指定します。例えば、@CookieValue("MyCookie") String myCookieValue
は、MyCookie
という名前のCookieの値をString
型のmyCookieValue
パラメータにバインドします。
型変換
Cookieの値は常に文字列として取得されますが、Springはこれをパラメータの型(プリミティブ型、ラッパー型、Stringなど)に自動的に変換しようとします。
必須・オプション設定 (required
) と デフォルト値 (defaultValue
)
@CookieValue
もrequired
属性とdefaultValue
属性を持ち、これらは@RequestParam
や@RequestHeader
と同様に機能します。
デフォルトではrequired = true
(必須)です。Cookieが存在しない場合にエラーとせず、オプションとして扱いたい場合はrequired = false
を設定します。オプションの場合、パラメータの型はラッパー型またはOptional
にする必要があります。
パラメータが提供されなかった場合にデフォルト値を設定したい場合は、defaultValue
属性を使用します。defaultValue
を指定すると、パラメータは自動的にオプション (required = false
相当) になります。
コンシューマブル/プロデューサブルメディアタイプ (consumes
, produces
)
HTTPリクエストやレスポンスには、そのボディの内容がどのような形式(メディアタイプ、またはMIMEタイプ)であるかを示す情報が含まれます。リクエストではContent-Type
ヘッダーで、レスポンスではAccept
ヘッダー(クライアントが受け付け可能な形式)およびContent-Type
ヘッダー(サーバーが実際に返信する形式)で指定されます。
@RequestMapping
アノテーションは、consumes
属性とproduces
属性を使用して、リクエストとレスポンスのメディアタイプに基づいてマッピングを限定することができます。これは、同じURLパスでも、異なる形式のデータを扱う場合に、異なるハンドラーメソッドを呼び出したい場合に非常に役立ちます(例: 同じ/users
でも、application/json
を受け付けるPOSTとapplication/xml
を受け付けるPOSTを分ける)。
consumes
属性
consumes
属性は、ハンドラーメソッドが処理できるリクエストボディのメディアタイプを指定します。SpringはリクエストのContent-Type
ヘッダーを見て、この属性にマッチするメソッドを探します。
値にはメディアタイプを表す文字列(例: "application/json"
, "text/plain"
, "application/xml"
)またはorg.springframework.http.MediaType
クラスの定数(例: MediaType.APPLICATION_JSON_VALUE
)を指定します。複数のメディアタイプを指定することも可能です。
“`java
package com.example.demo.controller;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(“/data”)
public class DataController {
// POST /data で、Content-Typeが application/json のリクエストを処理
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
public String handleJsonRequest(@RequestBody String jsonBody) {
System.out.println("Received JSON body: " + jsonBody);
return "Processed JSON";
}
// POST /data で、Content-Typeが application/xml のリクエストを処理
@PostMapping(consumes = MediaType.APPLICATION_XML_VALUE)
public String handleXmlRequest(@RequestBody String xmlBody) {
System.out.println("Received XML body: " + xmlBody);
return "Processed XML";
}
// 複数のContent-Typeを受け付ける場合
@PostMapping(value = "/text", consumes = {MediaType.TEXT_PLAIN_VALUE, MediaType.TEXT_HTML_VALUE})
public String handleTextOrHtml(@RequestBody String textBody) {
System.out.println("Received text or HTML body: " + textBody);
return "Processed Text/HTML";
}
}
“`
上記の例では、POST /data
に対して、Content-Type: application/json
のリクエストはhandleJsonRequest()
に、Content-Type: application/xml
のリクエストはhandleXmlRequest()
にマッピングされます。
produces
属性
produces
属性は、ハンドラーメソッドが生成できるレスポンスボディのメディアタイプを指定します。SpringはリクエストのAccept
ヘッダーを見て、この属性にマッチするメソッドを探します。また、レスポンスのContent-Type
ヘッダーをこの属性の値に設定します。
値にはメディアタイプを表す文字列またはMediaType
クラスの定数を指定します。複数のメディアタイプを指定することも可能です。その場合、クライアントのAccept
ヘッダーで指定された優先順位に基づいて、最も適切なメディアタイプが選択されます(Content Negotiation)。
“`java
package com.example.demo.controller;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(“/content”)
public class ContentController {
// GET /content で、Acceptヘッダーに応じて異なる形式のレスポンスを生成
// Accept: application/json の場合はこちら
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public String getJsonContent() {
System.out.println("Producing JSON response");
return "{\"message\": \"Hello from JSON!\"}";
}
// Accept: application/xml の場合はこちら
@GetMapping(produces = MediaType.APPLICATION_XML_VALUE)
public String getXmlContent() {
System.out.println("Producing XML response");
return "<message>Hello from XML!</message>";
}
// デフォルトとしてJSONを返す (最も一般的なケース)
// produces属性が指定されていない、または複数のproduces属性を持つメソッドがある場合、
// SpringはAcceptヘッダーに基づいて最適なメソッドを選択しようとします。
// 通常、produces属性は具体的なメディアタイプを指定し、そのメディアタイプをサポートする
// HttpMessageConverterが利用可能である必要があります。
@GetMapping("/default")
public Object getDefaultContent() {
// HttpMessageConverterによってJSONやXMLなどに変換されるオブジェクトを返す
return new SimpleObject("Default Message");
}
}
class SimpleObject {
private String message;
public SimpleObject(String message) { this.message = message; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}
“`
上記の例では、GET /content
に対して、Accept: application/json
ヘッダーを持つリクエストはgetJsonContent()
に、Accept: application/xml
ヘッダーを持つリクエストはgetXmlContent()
にマッピングされます。クライアントがAccept: application/json, application/xml
のように両方を受け付けることを示している場合、SpringはAccept
ヘッダーのQファクター(優先度)や、アプリケーションで設定されているHttpMessageConverter
の順序などを考慮して最適な形式を選択し、対応するメソッドを呼び出します。
@RestController
を使用する場合、戻り値は自動的に適切なHttpMessageConverter
によって指定されたproduces
のメディアタイプに変換されてレスポンスボディに書き込まれます。JSONやXMLの変換には通常、JacksonやJAXBなどのライブラリが必要です。SpringBootではこれらのライブラリは自動的に設定されます。
consumes
属性とproduces
属性は、クラスレベルとメソッドレベルの両方で指定可能です。メソッドレベルの指定は、クラスレベルの指定をオーバーライドします。
リクエストパラメータ/ヘッダーの条件 (params
, headers
)
@RequestMapping
は、リクエストパラメータやリクエストヘッダーの存在、あるいはその値に基づいてマッピングを限定することもできます。これは、同じパスに対しても、特定の条件を満たすリクエストだけを処理したい場合に便利です。例えば、APIのバージョンをヘッダーやクエリパラメータで指定する場合などに利用できます。
params
属性
params
属性は、特定のリクエストパラメータが存在するか、あるいは特定の値を持つかによってマッピングを限定します。値には以下の形式の文字列を指定します。
paramName
: リクエストパラメータparamName
が存在する場合にマッチ!paramName
: リクエストパラメータparamName
が存在しない場合にマッチparamName=paramValue
: リクエストパラメータparamName
が存在し、かつその値がparamValue
に等しい場合にマッチparamName!=paramValue
: リクエストパラメータparamName
が存在し、かつその値がparamValue
に等しくない場合にマッチ
“`java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(“/items”)
public class ItemController {
// GET /items?version=1 のみを処理
@GetMapping(params = "version=1")
public String getItemsV1(@RequestParam String version) {
return "Getting items for API version 1";
}
// GET /items?version=2 のみを処理
@GetMapping(params = "version=2")
public String getItemsV2(@RequestParam String version) {
return "Getting items for API version 2";
}
// GET /items (versionパラメータがない場合) を処理
@GetMapping(params = "!version")
public String getItemsDefault() {
return "Getting items (default version)";
}
// GET /items?status=active&type=physical のみを処理
@GetMapping(params = {"status=active", "type=physical"})
public String getActivePhysicalItems(@RequestParam String status, @RequestParam String type) {
return String.format("Getting active physical items (status: %s, type: %s)", status, type);
}
}
“`
この例では、/items
という同じパスに対するGETリクエストでも、version
クエリパラメータの値によって異なるメソッドが呼び出されます。params
属性は文字列の配列を受け取るため、複数の条件をAND条件として指定できます。
headers
属性
headers
属性は、特定のリクエストヘッダーが存在するか、あるいは特定の値を持つかによってマッピングを限定します。値にはparams
属性と同様の形式の文字列を指定します。ヘッダー名は大文字・小文字を区別しないマッチングが行われます。
headerName
: リクエストヘッダーheaderName
が存在する場合にマッチ!headerName
: リクエストヘッダーheaderName
が存在しない場合にマッチheaderName=headerValue
: リクエストヘッダーheaderName
が存在し、かつその値がheaderValue
に等しい場合にマッチheaderName!=headerValue
: リクエストヘッダーheaderName
が存在し、かつその値がheaderValue
に等しくない場合にマッチ
“`java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(“/api”)
public class ApiController {
// GET /api で、X-API-Version: 1 ヘッダーを持つリクエストのみを処理
@GetMapping(headers = "X-API-Version=1")
public String getApiV1(@RequestHeader("X-API-Version") String apiVersion) {
return "API Version 1 Response";
}
// GET /api で、X-API-Version: 2 ヘッダーを持つリクエストのみを処理
@GetMapping(headers = "X-API-Version=2")
public String getApiV2(@RequestHeader("X-API-Version") String apiVersion) {
return "API Version 2 Response";
}
// GET /api (X-API-Versionヘッダーがない場合) を処理
// Note: headers = "!X-API-Version" のマッチングは、上記の特定バージョンにマッチしなかった場合にのみ検討される
// Springのマッピング順序に注意が必要
@GetMapping(headers = "!X-API-Version")
public String getApiDefault() {
return "Default API Response";
}
// GET /api で、User-Agentヘッダーが特定の値を持つリクエストのみを処理
@GetMapping(headers = "User-Agent=MyApp")
public String getForMyApp(@RequestHeader("User-Agent") String userAgent) {
return "Response for MyApp client";
}
}
“`
headers
属性も文字列の配列を受け取り、複数の条件をAND条件として指定できます。
params
属性とheaders
属性は強力ですが、多用しすぎるとマッピングルールが複雑になり、どのメソッドが呼ばれるかを把握しにくくなる可能性があります。特にAPIバージョン管理においては、URIによるバージョン指定(例: /v1/users
, /v2/users
)の方がRESTfulの原則に近く、クライアント側も理解しやすいため推奨されることが多いです。ただし、ヘッダーによるバージョン指定など、特定のユースケースではこれらの属性が役立ちます。
パターンマッチング
@RequestMapping
のvalue
(またはpath
)属性では、静的なパスだけでなく、ワイルドカードを使ったパターンマッチングも利用できます。SpringはデフォルトでAntスタイルのパスパターンをサポートしており、柔軟なURLマッピングを可能にします。
Antスタイルのパスパターンで使えるワイルドカードは以下の通りです。
?
: 1文字にマッチします。*
: パスセグメント内(/
と/
の間、または/
と終端の間)の0文字以上の文字にマッチします。**
: 0個以上のパスセグメントにマッチします。これは通常、パターンパスの最後や、独立したパスセグメント(例:/path/**/anotherPath
)として使用されます。
“`java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(“/patterns”)
public class PatternMatchingController {
// /patterns/a?a にマッチ (例: /patterns/aaa, /patterns/aba)
@GetMapping("/a?a")
public String singleCharWildcard() {
return "'?' matched";
}
// /patterns/files/* にマッチ (例: /patterns/files/report.pdf, /patterns/files/image.png)
// ただし、/patterns/files/some/report.pdf にはマッチしない
@GetMapping("/files/*")
public String segmentWildcard() {
return "'*' matched in segment";
}
// /patterns/any/path/here/... にマッチ (例: /patterns/any/a, /patterns/any/a/b, /patterns/any/a/b/c)
// **は複数のパスセグメントにマッチ
@GetMapping("/any/**")
public String multiSegmentWildcard() {
return "'**' matched multiple segments";
}
// /patterns/users/{id} にマッチするが、IDは数値のみを許可する場合
// パス変数と組み合わせることも多い
@GetMapping("/users/{id:\\d+}") // IDが1つ以上の数字であることを要求
public String getUserById(@PathVariable Long id) {
return "Matched user id (numeric): " + id;
}
// ** を使った例: /patterns/docs/2023/report.txt など、任意階層のファイルにマッチ
@GetMapping("/docs/**/{fileName}")
public String getDocFile(@PathVariable String fileName) {
// 注意: ** とパス変数を組み合わせる場合、Springは最も具体的なマッチングを選択します。
// fileNameだけではパス全体は取得できません。パス全体を取得するには HttpServletRequest などを利用します。
return "Matched doc file with name: " + fileName;
}
}
“`
パス変数とパターンマッチング
上記の例のように、パス変数とワイルドカードや正規表現を組み合わせて使うことも可能です。{変数名:正規表現}
の形式でパス変数に正規表現を適用すると、そのパスセグメントが正規表現にマッチする場合にのみマッピングが成功します。
マッチングの優先順位
複数の@RequestMapping
パターンが1つのリクエストURLにマッチする場合、Springは最も具体的なパターンを持つハンドラーメソッドを優先します。具体性の判断は、静的なパスセグメントの数、ワイルドカードの種類(?
< *
< **
)、パス変数の有無などに基づいて行われます。例えば、/users/123
というリクエストに対して、/users/{id}
というパターンは /users/*
や /users/**
よりも具体的であると判断され、優先されます。正規表現を含むパス変数は、含まないパス変数よりも具体的とみなされる場合があります。
**
ワイルドカードは最も包括的なパターンであり、通常は最も低い優先順位と見なされます。そのため、より具体的な静的パスや他のワイルドカードパターンが優先されます。
Trailing Slash Matching (末尾スラッシュのマッチング)
Spring MVCでは、歴史的に/users
と/users/
のような末尾にスラッシュがあるかないかのURLを同じものとみなす設定がデフォルトで行われていました。しかし、この動作は厳密なURLマッチングやRESTful原則と乖離する場合があるため、Spring Boot 2.6からはデフォルトで無効になっています。
現在のデフォルト設定(Spring Boot 2.6以降)では、/users
という@GetMapping
は/users/
というリクエストにはマッチしません。もし両方のリクエストを受け付けたい場合は、明示的に両方のパターンを指定する必要があります。
“`java
// Spring Boot 2.6+ のデフォルト設定では、これは /users にのみマッチ
@GetMapping(“/users”)
public String getUsers() {
return “Users list”;
}
// /users と /users/ の両方にマッチさせたい場合
@GetMapping({“/users”, “/users/”})
public String getUsersAndTrailingSlash() {
return “Users list (with/without trailing slash)”;
}
“`
または、アプリケーションの設定でこの挙動を変更することも可能ですが、混乱を避けるためにも、明示的にパスを指定するか、新しいデフォルトの挙動に合わせたURL設計を行うことが推奨されます。
その他の属性
@RequestMapping
には、これまで説明した主要な属性の他に、いくつかの属性があります。
name
: マッピングに名前を付けるための属性です。これは主にデバッグやログ出力、フレームワークの内部処理(例: メソッドのリフレクション情報のキャッシュキーとして使用されるなど)で利用されることがあります。URLを生成するためのリバースルーティングの用途など、名前付きマッピングを特定のフレームワーク機能と連携させる場合にも役立ちますが、アプリケーションコード内で直接的に必須となるケースは少ないかもしれません。
java
@GetMapping(value = "/resource/{id}", name = "getResourceById")
public String getResource(@PathVariable Long id) {
// ...
return "Resource " + id;
}
name
属性は任意であり、省略してもマッピングは正常に機能します。
@RequestMapping
とSpring Boot
Spring BootはSpring Frameworkをベースにしており、@RequestMapping
アノテーションはSpring Bootアプリケーションでも中心的な役割を果たします。SpringBootは開発者が迅速にSpringアプリケーションを構築できるように、多くの設定を自動で行います。@RequestMapping
に関連する設定も、これらの自動設定の恩恵を受けています。
Spring Bootアプリケーションでは、@SpringBootApplication
アノテーションが付いたメインクラスを含むパッケージ、およびそのサブパッケージがComponent Scanの対象となります。@Controller
や@RestController
アノテーションが付いたクラスはSpring Beanとして管理され、その中の@RequestMapping
(またはショートカットアノテーション)が付いたメソッドがリクエストハンドラーとして登録されます。
Spring BootのWebスターター(spring-boot-starter-web
)を使用すると、Spring MVCに必要な依存関係(DispatcherServlet、組み込みTomcat/Jetty/Undertow、JSON処理ライブラリなど)と自動設定が提供されます。これにより、開発者はXML設定ファイルなどをほとんど書くことなく、@RequestMapping
アノテーションを使ってすぐにWebエンドポイントを定義し始めることができます。
特にRESTful API開発において、@RestController
と@RequestMapping
(またはショートカットアノテーション)の組み合わせは非常に一般的です。@RestController
を使うことで、クラス内のすべてのメソッドがデフォルトで@ResponseBody
の挙動をするため、JSONやXMLなどのデータを返すAPIエンドポイントを簡単に実装できます。また、Spring Bootの自動設定により、Jacksonなどのライブラリが自動的に設定され、JavaオブジェクトとJSON/XML間の変換が透過的に行われます。
“`java
package com.example.demo; // トップレベルパッケージ
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication // Spring Bootアプリケーションのエントリーポイント
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
// 別ファイル、または同じファイル内 (通常は別ファイル)
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController // このクラス内のメソッドは @ResponseBody が付与されたとして扱われる
@RequestMapping(“/api/greeters”) // クラスレベルのベースパス
public class GreeterController {
// GET /api/greeters/hello/{name}
@GetMapping("/hello/{name}") // メソッドレベルのパスとパス変数
public Greeting greet(@PathVariable String name) {
// 戻り値のGreetingオブジェクトは自動的にJSONに変換される (Jacksonがあれば)
return new Greeting("Hello", name);
}
// GET /api/greeters/goodbye
@GetMapping("/goodbye")
public Greeting goodbye() {
return new Greeting("Goodbye", "World");
}
}
// 戻り値として使用する簡単なPOJO
class Greeting {
private String greeting;
private String target;
// JacksonなどがJSON <=> Object変換に必要とするデフォルトコンストラクタ (必要に応じて)
public Greeting() {}
public Greeting(String greeting, String target) {
this.greeting = greeting;
this.target = target;
}
// GetterとSetter (JSON変換に必要)
public String getGreeting() { return greeting; }
public void setGreeting(String greeting) { this.greeting = greeting; }
public String getTarget() { return target; }
public void setTarget(String target) { this.target = target; }
}
“`
上記のSpring Bootアプリケーション例では、@SpringBootApplication
がcom.example.demo
パッケージとそのサブパッケージをスキャンし、@RestController
が付いたGreeterController
をBeanとして登録します。GreeterController
内の@GetMapping
が付いたメソッドがリクエストハンドラーとして機能し、それぞれのメソッドの戻り値(Greeting
オブジェクト)は、Spring Bootが自動設定したHttpMessageConverter
(通常はJackson)によってJSON形式に変換され、HTTPレスポンスボディとして返されます。
このように、Spring Bootは@RequestMapping
を使ったWeb開発を非常にシンプルかつ効率的に行えるように、必要なインフラストラクチャを自動的に提供してくれます。
実践的なヒントとベストプラクティス
@RequestMapping
は非常に柔軟ですが、適切に使用しないとコードが読みにくくなったり、保守が難しくなったりします。ここでは、@RequestMapping
を効果的に使用するための実践的なヒントとベストプラクティスを紹介します。
1. クラスレベルとメソッドレベルの組み合わせを活用する
共通のベースパスを持つエンドポイントをまとめるには、クラスレベルの@RequestMapping
が非常に有効です。これにより、各メソッドの@RequestMapping
のパス指定を短く保ち、コントローラーの役割を明確にできます。
例: /api/v1/users
に関連するエンドポイントはUserController
にまとめ、クラスレベルで@RequestMapping("/api/v1/users")
を指定する。
2. ショートカットアノテーション (@GetMapping
など) を積極的に使用する
可読性とメンテナンス性を向上させるため、@RequestMapping(method = ...)
の代わりに@GetMapping
, @PostMapping
, @PutMapping
, @DeleteMapping
, @PatchMapping
を使用することを強く推奨します。これは現代のSpring開発における標準的なプラクティスです。
3. RESTfulなURL設計を心がける
@RequestMapping
を使ってエンドポイントを設計する際は、RESTfulの原則に従うことが望ましいです。
- URIはリソースを表す名詞にする(例:
/users
,/products
)。 - HTTPメソッドで操作を表す(GET: 取得、POST: 作成、PUT: 全体更新、PATCH: 部分更新、DELETE: 削除)。
- リソースの階層構造をURIで表現する(例:
/users/{userId}/orders/{orderId}
)。 - CRUD以外の操作は動詞をURIに含めることも許容される場合がある(例:
/products/{productId}/purchase
)が、可能な限りリソースとHTTPメソッドで表現することを検討する。
4. APIバージョン管理の検討
APIのバージョン管理は、後方互換性を維持しつつAPIを進化させる上で重要です。@RequestMapping
は、バージョン管理のいくつかの方法に対応できます。
-
URIによるバージョン指定: 最も一般的で推奨される方法です。クラスレベルの
@RequestMapping
でバージョンを付加します。例:
“`java
@RestController
@RequestMapping(“/api/v1/users”) // バージョンをURIに含める
public class UserControllerV1 { … }@RestController
@RequestMapping(“/api/v2/users”)
public class UserControllerV2 { … }
``
headers
* **Headerによるバージョン指定**:属性を利用します。例:
headers = “X-API-Version=1”。URIがシンプルになる利点がありますが、クライアントがカスタムヘッダーを扱いやすいか、ドキュメントで明確にする必要があるかなどを考慮する必要があります。
params
* **Query Parameterによるバージョン指定**:属性を利用します。例:
params = “version=1″`。シンプルですが、URIが冗長になりやすく、RESTful原則からは少し外れる傾向があります。
どの方法を選択するかはプロジェクトの要件によりますが、URIによるバージョン指定が最も分かりやすく、@RequestMapping
とも自然に連携できます。
5. パス変数やリクエストパラメータの名前を明確にする
@PathVariable
や@RequestParam
を使用する際は、パラメータ名がその意味を明確に表していることを確認してください。必要であれば、value
属性で明示的に指定します。
6. consumes
および produces
を適切に利用する
JSONやXMLなど、特定のメディアタイプのみを処理/生成するAPIエンドポイントでは、consumes
やproduces
属性を明示的に指定することで、意図しないリクエスト形式を拒否したり、ドキュメントを明確にしたりすることができます。特にRESTful APIではproduces = MediaType.APPLICATION_JSON_VALUE
などを指定することが一般的です。
7. エラーハンドリングとの連携を考慮する
@RequestMapping
でマッピングされたハンドラーメソッド内で例外が発生した場合、Springの例外ハンドリングメカニズム(@ExceptionHandler
, @ControllerAdvice
など)によって捕捉・処理されます。これらの例外ハンドラーも特定のリクエストパスや例外タイプに紐づけることが可能ですが、通常は@ControllerAdvice
を使ったグローバルな例外ハンドリングを設定することで、@RequestMapping
を持つすべてのコントローラーメソッドからの例外を一元的に処理できます。
8. ドキュメンテーションツールとの連携
Swagger/OpenAPIなどのAPIドキュメンテーションツールは、@RequestMapping
アノテーションの情報(パス、HTTPメソッド、パラメータ、consumes/producesなど)を読み取り、API仕様書を自動生成します。適切な@RequestMapping
の使用は、正確なAPIドキュメント生成の基盤となります。
9. パターンマッチングは慎重に使う
**
のような広範なワイルドカードは強力ですが、予期しないリクエストにマッチしてしまう可能性があります。より具体的なパス指定を優先し、ワイルドカードは必要な範囲でのみ、意図が明確になるように使用することが推奨されます。
@RequestMapping
の代替手段/関連アノテーション
@RequestMapping
はSpring MVC/Bootにおける主要なマッピングアノテーションですが、それ単独で使われることは少なく、他のアノテーションと組み合わせて使われるか、または代替となるアノテーションが存在します。
@RestController
と@ResponseBody
既に何度か触れましたが、@RestController
は@Controller
と@ResponseBody
を組み合わせたアノテーションです。@Controller
はModelAndViewを返してViewの名前を解決するビュー指向のコントローラーで使われることが多いのに対し、@RestController
はメソッドの戻り値をHTTPレスポンスボディに直接書き込むRESTful API向けに設計されています。@RestController
を使うと、そのクラス内のすべての@RequestMapping
(またはショートカットアノテーション)を持つメソッドが自動的に@ResponseBody
の動作をするようになります。RESTful APIを開発する場合は、通常@RestController
を使用します。
“`java
@RestController // このクラスのメソッドはデフォルトで @ResponseBody の動作
@RequestMapping(“/api/users”)
public class RestUserController {
// このメソッドの戻り値(Userオブジェクト)は自動的にJSONなどに変換されてレスポンスボディに書き込まれる
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) { ... }
}
“`
@RequestBody
@RequestBody
アノテーションは、HTTPリクエストボディの内容をハンドラーメソッドのパラメータとして受け取るために使用されます。リクエストボディは、JSONやXMLなどの形式でデータを送信する際(特にPOSTやPUTリクエスト)に利用されます。Springは、HttpMessageConverter
を使用して、リクエストボディを指定されたパラメータの型に変換します。
java
// @RestController環境下
@PostMapping("/products")
public Product createProduct(@RequestBody Product product) { // リクエストボディ(例:JSON)をProductオブジェクトに変換して受け取る
// ...
return product;
}
@RequestMapping
のconsumes
属性と@RequestBody
は密接に関連しています。consumes
で指定されたメディアタイプに対応するHttpMessageConverter
が、@RequestBody
で指定されたパラメータの型への変換を試みます。
その他のアノテーション
Spring MVC/Bootには、リクエストからデータを取得するための他のアノテーションも多数存在します。
@ModelAttribute
: クエリパラメータやフォームデータ、あるいはパス変数やセッション属性からオブジェクトにまとめてデータをバインドします。GETリクエストの複雑なフィルタリング条件をオブジェクトで受け取る場合などに便利です。また、ModelAndViewに自動的に属性として追加されるため、Viewにデータを渡す際にも利用されます。@SessionAttribute
: HTTPセッションから属性値を取得します。@RequestAttribute
:HttpServletRequest
のリクエスト属性から値を取得します。@MatrixVariable
: マトリックス変数(URLパスの一部に;param=value
の形式で埋め込まれる変数)を取得します。これはRESTfulな設計の一つのスタイルですが、あまり一般的ではありません。
これらのアノテーションは、@RequestMapping
でリクエストが特定のハンドラーメソッドにマッピングされた後、そのメソッドのパラメータにリクエストの様々な部分をバインドするために使用されます。つまり、@RequestMapping
が「どのメソッドを呼び出すか」を決定し、これらのアノテーションが「呼び出されたメソッドにどのようなデータを渡すか」を決定する役割を担います。
まとめ
この記事では、Spring MVCおよびSpring Bootにおける@RequestMapping
アノテーションについて、その基本的な使い方から詳細な機能までを網羅的に解説しました。@RequestMapping
は、リクエストパス、HTTPメソッド、リクエストパラメータ、ヘッダー、Cookie、メディアタイプなど、多岐にわたる条件に基づいてHTTPリクエストをハンドラーメソッドにマッピングする、Spring Web開発の中核をなすアノテーションです。
- クラスレベルとメソッドレベルで適用することで、ベースパスと個別パスを組み合わせて定義できます。
method
属性または専用のショートカットアノテーション(@GetMapping
など)で、HTTPメソッドを限定したマッピングが可能です。特にショートカットアノテーションは可読性が高く推奨されます。@PathVariable
でURLパスの動的な値を、@RequestParam
でクエリパラメータやフォームデータを、@RequestHeader
でリクエストヘッダーを、@CookieValue
でCookieの値をハンドラーメソッドのパラメータとして簡単に取得できます。これらのアノテーションは型変換、必須/オプション設定、デフォルト値指定の機能を提供します。consumes
属性とproduces
属性で、リクエスト/レスポンスのメディアタイプに基づいてマッピングを限定できます。これにより、Content Negotiationを実装したり、異なる形式のデータを扱うエンドポイントを区別したりできます。params
属性とheaders
属性で、リクエストパラメータやヘッダーの存在や値に基づいて、より詳細なマッピング条件を設定できます。- Antスタイルのパスパターン(
*
,**
,?
)や正規表現を組み合わせることで、柔軟なURLパターンマッチングが可能です。 - Spring Boot環境では、
@RequestMapping
は自動設定されたWebインフラストラクチャとシームレスに連携し、特に@RestController
と組み合わせることでRESTful APIの開発を効率化します。
@RequestMapping
の機能を深く理解し、本記事で紹介した様々な属性や関連アノテーション、そして実践的なヒントを活用することで、あなたはSpring MVC/Bootアプリケーションにおいて、要求仕様に正確に合致し、かつ保守性の高いWebエンドポイントを設計・実装できるようになります。
Spring Web開発の旅は続きます。@RequestMapping
はその強力な羅針盤となるでしょう。ぜひ実際にコードを書きながら、その機能と柔軟性を体感してください。