Springのリクエストマッピング

Spring RequestMapping

1. 概要

この記事では、Spring MVC – @RequestMapping.の主要なアノテーションの1つに焦点を当てます。

簡単に言うと、注釈はWeb要求をSpring Controllerメソッドにマップするために使用されます。

参考文献:

Springで静的リソースを提供する

Spring MVCで静的リソースをマッピングおよび処理する方法-単純な構成を使用し、3.1より柔軟な構成を使用し、最後に新しい4.1リソースリゾルバーを使用します。

Spring MVCでフォームを使い始める

Spring MVCを使用してフォームを操作する方法を学びます-基本エンティティのマッピング、送信、エラーの表示。

Spring Frameworkを使用したHTTPメッセージコンバーター

SpringでREST APIのHttpMessageConvertersを構成する方法と、RestTemplateでこれらのコンバーターを使用する方法。

2. @RequestMappingの基本

簡単な例から始めましょう–いくつかの基本的な基準を使用してHTTPリクエストをメソッドにマッピングします。

2.1. @RequestMapping –パス別

@RequestMapping(value = "/ex/foos", method = RequestMethod.GET)
@ResponseBody
public String getFoosBySimplePath() {
    return "Get some Foos";
}

単純なcurlコマンドでこのマッピングをテストするには、次のコマンドを実行します。

curl -i http://localhost:8080/spring-rest/ex/foos

2.2. @RequestMapping –HTTPメソッド

HTTPmethodパラメータにはno defaultが含まれているため、値を指定しない場合は、任意のHTTPリクエストにマッピングされます。

これは前の例と同様の簡単な例ですが、今回はHTTPPOSTリクエストにマッピングされています。

@RequestMapping(value = "/ex/foos", method = POST)
@ResponseBody
public String postFoos() {
    return "Post some Foos";
}

curlコマンドを使用してPOSTをテストするには:

curl -i -X POST http://localhost:8080/spring-rest/ex/foos

3. RequestMappingとHTTPヘッダー

3.1. headers属性を持つ@RequestMapping

リクエストのヘッダーを指定することで、マッピングをさらに絞り込むことができます。

@RequestMapping(value = "/ex/foos", headers = "key=val", method = GET)
@ResponseBody
public String getFoosWithHeader() {
    return "Get some Foos with Header";
}

操作をテストするために、curlヘッダーサポートを使用します。

curl -i -H "key:val" http://localhost:8080/spring-rest/ex/foos

また、@RequestMappingheader属性を介した複数のヘッダーもあります。

@RequestMapping(
  value = "/ex/foos",
  headers = { "key1=val1", "key2=val2" }, method = GET)
@ResponseBody
public String getFoosWithHeaders() {
    return "Get some Foos with Header";
}

次のコマンドでこれをテストできます。

curl -i -H "key1:val1" -H "key2:val2" http://localhost:8080/spring-rest/ex/foos

ヘッダーキーとヘッダー値を区切るためのcurl構文では、HTTP仕様と同じようにコロンですが、Springでは等号が使用されることに注意してください。

3.2. @RequestMappingは消費し、生成します

media types produced by a controllerメソッドのマッピングは、特に注意する価値があります。上記で紹介した@RequestMappingヘッダー属性を介して、Acceptヘッダーに基づいてリクエストをマッピングできます。

@RequestMapping(
  value = "/ex/foos",
  method = GET,
  headers = "Accept=application/json")
@ResponseBody
public String getFoosAsJsonFromBrowser() {
    return "Get some Foos with Header Old";
}

Acceptヘッダーを定義するこの方法のマッチングは柔軟です。equalsの代わりにcontainsを使用するため、次のようなリクエストでも正しくマップされます。

curl -H "Accept:application/json,text/html"
  http://localhost:8080/spring-rest/ex/foos

Spring 3.1以降、特にこの目的のための@RequestMapping annotation now has the produces and the consumes attributes

@RequestMapping(
  value = "/ex/foos",
  method = RequestMethod.GET,
  produces = "application/json"
)
@ResponseBody
public String getFoosAsJsonFromREST() {
    return "Get some Foos with Header New";
}

また、headers属性を持つ古いタイプのマッピングは、Spring 3.1以降の新しいproducesメカニズムに自動的に変換されるため、結果は同じになります。

これは、同じ方法でcurlを介して消費されます。

curl -H "Accept:application/json"
  http://localhost:8080/spring-rest/ex/foos

さらに、producesは複数の値もサポートします。

@RequestMapping(
  value = "/ex/foos",
  method = GET,
  produces = { "application/json", "application/xml" }
)

これら(acceptヘッダーを指定する古い方法と新しい方法)は基本的に同じマッピングであるため、Springはそれらを一緒に許可しないことに注意してください。これらの両方のメソッドをアクティブにすると、次のようになります。

Caused by: java.lang.IllegalStateException: Ambiguous mapping found.
Cannot map 'fooController' bean method
java.lang.String
org.example.spring.web.controller
  .FooController.getFoosAsJsonFromREST()
to
{ [/ex/foos],
  methods=[GET],params=[],headers=[],
  consumes=[],produces=[application/json],custom=[]
}:
There is already 'fooController' bean method
java.lang.String
org.example.spring.web.controller
  .FooController.getFoosAsJsonFromBrowser()
mapped.

新しいproducesおよびconsumesメカニズムに関する最後の注意–これらは、他のほとんどのアノテーションとは異なる動作をします。タイプレベルで指定すると、the method level annotations do not complement but overrideはタイプレベルの情報です。

そしてもちろん、Springを使用したREST APIの構築をさらに深く掘り下げたい場合は–check outthe new REST with Spring course

4. パス変数を含むRequestMapping

マッピングURIの一部は、@PathVariableアノテーションを介して変数にバインドできます。

4.1. 単一の@PathVariable

単一のパス変数を使用した簡単な例:

@RequestMapping(value = "/ex/foos/{id}", method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariable(
  @PathVariable("id") long id) {
    return "Get a specific Foo with id=" + id;
}

これはcurlでテストできます:

curl http://localhost:8080/spring-rest/ex/foos/1

メソッドパラメータの名前がパス変数の名前と正確に一致する場合、これはusing @PathVariable with no valueで簡略化できます。

@RequestMapping(value = "/ex/foos/{id}", method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariable(
  @PathVariable String id) {
    return "Get a specific Foo with id=" + id;
}

@PathVariableは自動型変換の恩恵を受けるため、idを次のように宣言することもできます。

@PathVariable long id

4.2. 複数の@PathVariable

より複雑なURIでは、URIの複数の部分をmultiple valuesにマップする必要がある場合があります。

@RequestMapping(value = "/ex/foos/{fooid}/bar/{barid}", method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariables
  (@PathVariable long fooid, @PathVariable long barid) {
    return "Get a specific Bar with id=" + barid +
      " from a Foo with id=" + fooid;
}

これは、同じ方法でcurlを使用して簡単にテストできます。

curl http://localhost:8080/spring-rest/ex/foos/1/bar/2

4.3. 正規表現を使用した@PathVariable

@PathVariableをマッピングするときに、正規表現を使用することもできます。たとえば、idの数値のみを受け入れるようにマッピングを制限します。

@RequestMapping(value = "/ex/bars/{numericId:[\\d]+}", method = GET)
@ResponseBody
public String getBarsBySimplePathWithPathVariable(
  @PathVariable long numericId) {
    return "Get a specific Bar with id=" + numericId;
}

これは、次のURIが一致することを意味します。

http://localhost:8080/spring-rest/ex/bars/1

しかし、これはしません:

http://localhost:8080/spring-rest/ex/bars/abc

5. リクエストパラメータを含むRequestMapping

@RequestMappingを使用すると、mapping of URL parameters with the @RequestParam annotationを簡単に使用できます。 __

現在、リクエストを次のようなURIにマッピングしています。

http://localhost:8080/spring-rest/ex/bars?id=100
@RequestMapping(value = "/ex/bars", method = GET)
@ResponseBody
public String getBarBySimplePathWithRequestParam(
  @RequestParam("id") long id) {
    return "Get a specific Bar with id=" + id;
}

次に、コントローラーメソッドシグネチャの@RequestParam(“id”)アノテーションを使用して、idパラメーターの値を抽出しています。

idパラメータを使用してリクエストを送信するには、curlのパラメータサポートを使用します。

curl -i -d id=100 http://localhost:8080/spring-rest/ex/bars

この例では、パラメーターは最初に宣言されずに直接バインドされました。

より高度なシナリオの場合、@RequestMapping can optionally define the parameters –リクエストマッピングを絞り込むさらに別の方法として:

@RequestMapping(value = "/ex/bars", params = "id", method = GET)
@ResponseBody
public String getBarBySimplePathWithExplicitRequestParam(
  @RequestParam("id") long id) {
    return "Get a specific Bar with id=" + id;
}

さらに柔軟なマッピングが可能です。複数のparams値を設定でき、すべてを使用する必要はありません。

@RequestMapping(
  value = "/ex/bars",
  params = { "id", "second" },
  method = GET)
@ResponseBody
public String getBarBySimplePathWithExplicitRequestParams(
  @RequestParam("id") long id) {
    return "Narrow Get a specific Bar with id=" + id;
}

そしてもちろん、次のようなURIへのリクエスト:

http://localhost:8080/spring-rest/ex/bars?id=100&second=something

常にベストマッチにマッピングされます。これは、idパラメーターとsecondパラメーターの両方を定義するより狭いマッチです。

6. RequestMappingコーナーケース

6.1. @RequestMapping –同じコントローラーメソッドにマップされた複数のパス

通常、単一の@RequestMappingパス値が単一のコントローラーメソッドに使用されますが、これは厳密なルールではなく、単なる良い習慣です。同じメソッドに複数のリクエストをマッピングする必要がある場合があります。 その場合、1つだけではなくthe value attribute of @RequestMapping does accept multiple mappings

@RequestMapping(
  value = { "/ex/advanced/bars", "/ex/advanced/foos" },
  method = GET)
@ResponseBody
public String getFoosOrBarsByPath() {
    return "Advanced - Get some Foos or Bars";
}

これで、これらのcurlコマンドの両方が同じメソッドにヒットするはずです。

curl -i http://localhost:8080/spring-rest/ex/advanced/foos
curl -i http://localhost:8080/spring-rest/ex/advanced/bars

6.2. @RequestMapping –同じコントローラーメソッドへの複数のHTTPリクエストメソッド

異なるHTTP動詞を使用する複数の要求は、同じコントローラーメソッドにマップできます。

@RequestMapping(
  value = "/ex/foos/multiple",
  method = { RequestMethod.PUT, RequestMethod.POST }
)
@ResponseBody
public String putAndPostFoos() {
    return "Advanced - PUT and POST within single method";
}

curlを使用すると、これらの両方が同じメソッドにヒットします。

curl -i -X POST http://localhost:8080/spring-rest/ex/foos/multiple
curl -i -X PUT http://localhost:8080/spring-rest/ex/foos/multiple

6.3. @RequestMapping –すべてのリクエストのフォールバック

特定のHTTPメソッドを使用して、すべてのリクエストに対して単純なフォールバックを実装するには-たとえば、GETの場合:

@RequestMapping(value = "*", method = RequestMethod.GET)
@ResponseBody
public String getFallback() {
    return "Fallback for GET Requests";
}

または、すべてのリクエストでも:

@RequestMapping(
  value = "*",
  method = { RequestMethod.GET, RequestMethod.POST ... })
@ResponseBody
public String allFallback() {
    return "Fallback for All Requests";
}

6.4. あいまいなマッピングエラー

曖昧なマッピングエラーは、Springが2つ以上のリクエストマッピングを異なるコントローラーメソッドで同じであると評価するときに発生します。 要求のマッピングは、HTTPメソッド、URL、パラメーター、ヘッダー、およびメディアタイプが同じ場合も同じです。 たとえば、これはあいまいなマッピングです。

@GetMapping(value = "foos/duplicate")
public String duplicate() {
    return "Duplicate";
}

@GetMapping(value = "foos/duplicate")
public String duplicateEx() {
    return "Duplicate";
}

通常、スローされる例外には、次の行に沿ってエラーメッセージがあります。

Caused by: java.lang.IllegalStateException: Ambiguous mapping.
  Cannot map 'fooMappingExamplesController' method
  public java.lang.String org.example.web.controller.FooMappingExamplesController.duplicateEx()
  to {[/ex/foos/duplicate],methods=[GET]}:
  There is already 'fooMappingExamplesController' bean method
  public java.lang.String org.example.web.controller.FooMappingExamplesController.duplicate() mapped.

エラーメッセージを注意深く読むと、Springはメソッドorg.example.web.controller.FooMappingExamplesController.duplicateEx() をマッピングできないことがわかります。これは、すでにマッピングされているorg.example.web.controller.FooMappingExamplesController.duplicate().と競合するマッピングがあるためです。

以下のコードスニペットでは、両方のメソッドが異なるコンテンツタイプを返すため、あいまいなマッピングエラーは発生しません。

@GetMapping(value = "foos/duplicate/xml", produces = MediaType.APPLICATION_XML_VALUE)
public String duplicateXml() {
    return "Duplicate";
}

@GetMapping(value = "foos/duplicate/json", produces = MediaType.APPLICATION_JSON_VALUE)
public String duplicateJson() {
    return "Duplicate";
}

これを解決する明白な方法は、関連する2つの方法のいずれかに割り当てられたURLを更新することです。

7. 新しいリクエストマッピングのショートカット

Spring Framework 4.3では、すべて@RequestMappingに基づくa few newHTTPマッピングアノテーションが導入されました。

  • @GetMapping

  • @PostMapping

  • @PutMapping

  • @DeleteMapping

  • @PatchMapping

これらの新しい注釈により、読みやすさが向上し、コードの冗長性が低下します。 CRUD操作をサポートするRESTful APIを作成して、これらの新しいアノテーションの動作を見てみましょう。

@GetMapping("/{id}")
public ResponseEntity getBazz(@PathVariable String id){
    return new ResponseEntity<>(new Bazz(id, "Bazz"+id), HttpStatus.OK);
}

@PostMapping
public ResponseEntity newBazz(@RequestParam("name") String name){
    return new ResponseEntity<>(new Bazz("5", name), HttpStatus.OK);
}

@PutMapping("/{id}")
public ResponseEntity updateBazz(
  @PathVariable String id,
  @RequestParam("name") String name) {
    return new ResponseEntity<>(new Bazz(id, name), HttpStatus.OK);
}

@DeleteMapping("/{id}")
public ResponseEntity deleteBazz(@PathVariable String id){
    return new ResponseEntity<>(new Bazz(id), HttpStatus.OK);
}

これらの詳細はhereにあります。

8. スプリング構成

Spring MVCの構成は非常に単純です。FooControllerが次のパッケージで定義されていることを考慮してください。

package org.example.spring.web.controller;

@Controller
public class FooController { ... }

完全なMVCサポートを有効にし、コントローラーのクラスパススキャンを構成するには、@Configurationクラスが必要です。

@Configuration
@EnableWebMvc
@ComponentScan({ "org.example.spring.web.controller" })
public class MvcConfig {
    //
}

9. 結論

この記事では、@RequestMapping annotation in Springに焦点を当てます。簡単な使用例、HTTPヘッダーのマッピング、URIの一部と@PathVariableのバインド、URIパラメーターと@RequestParamアノテーションの操作について説明します。

Spring MVCで別のコアアノテーションを使用する方法を学びたい場合は、the @ModelAttribute annotation hereを調べることができます。

記事の完全なコードはon GitHubで入手できます。 これはMavenプロジェクトであるため、そのままインポートして実行するのは簡単です。