読者です 読者をやめる 読者になる 読者になる

M12i.

学術書・マンガ・アニメ・映画の消費活動とプログラミングについて

Spring MVCのドキュメント「コントローラを実装する」を読む(1)

f:id:m12i:20141115150916p:plain

今回はSpring Web MVCフレームワークに関するドキュメントの訳出です。Spring Bootフレームワークのリファレンスに言及されていたことで関心を持ち、ちょっとばかし読んでみました。

原典は、Springフレームワーク(本体)のリファレンス・マニュアルである"Spring Framework Reference Documentation"の第5部"The Web"の第17章"Web MVC framework"の第3節(バージョン4.1.1.RELEASE現在)です。

          * * *

17.3 コントローラを実装する(1)

コントローラは〔Webアプリケーションのユーザに対して〕アプリケーションの振る舞い──多くの場合サービス・インターフェースにより定義される──へのアクセスを提供します。コントローラはユーザの入力値を読み取り、モデル──ビューによってユーザに表示される──に変換します。Springでは、コントローラは非常に抽象的なかたちで実装されており、多種多少なコントローラを生成することができます。

Spring 2.5では、MVCコントローラのためにアノテーション──@RequestMapping@RequestParam@ModelAttributeなどなど──に基づくプログラミング・モデルの機能が導入されました。このアノテーション・サポートはSerlvet MVCPortlet MVCのいずれにおいても利用可能です。この機能で実装されるコントローラは、特定のベース・クラスを拡張したり、特定のインターフェースを実装したりする必要がありません。加えて、それらのオブジェクトはServletPortletの機能に容易にアクセスできますが、たいていの場合、ServletPortletAPIにの直接の依存関係も持つことがありません。

[NOTE]  Github上のspring-projectsでは、このセクションで説明しているアノテーション・サポートの機能を利用したWebアプリケーションとして、MvcShowcase、MvcAjax、MvcBasic、PetClinic、PetCare、その他多くのサンプルが参照できます。

@Controller
public class HelloWorldController {

    @RequestMapping("/helloWorld")
    public String helloWorld(Model model) {
        model.addAttribute("message", "Hello World!");
        return "helloWorld";
    }
}

ご覧のとおり、@Controller @RequestMapping という2つのアノテーションメソッド名とシグネチャに柔軟性をもたらします。この例では、メソッド Model を引数としてとり、Stringでビューの名称を返しています。しかしながらこのセクションの後のほうで説明しているように、メソッド・パラメータと戻り値にはこれと異なる多くのパターンが利用可能です。@Controller @RequestMapping 、そしてその他多くのアノテーションは、Spring MVCの実装の基盤をなしています。このセクションでは、これらのアノテーションとそれがサーブレット環境においてどのように使用されるのか、そのもっとも一般的なケースについて説明します。

17.3.1 @Controllerによりコントローラを定義する

@Controller アノテーションはあるクラスがコントローラの役目を負うことを示すために使用されます。Springはコントローラのために、何かしらのベース・クラスを拡張したり、サーブレットAPIを参照したりすることを強要しません。それでも、もし必要とあらば、サーブレット固有の機能にアクセスすることが可能です。

@Controllerアノテーションはそれが付与されたクラスのステレオタイプとして振る舞い、そのロールを示します。 ディスパッチャはマッピングされたメソッドを検索するためこれらのアノテーションが付与されたクラスをスキャンし、@RequestMappingアノテーションを検知します(このアノテーションについては次のセクションを参照)。

ディスパッチャー・コンテキストのなかで、標準的なSpringのビーン定義を使用して、アノテーションを付与されたコントローラをビーンとして明示的に定義することも可能です。しかしながら@Controllerステレオタイプの処理は自動検知──Springがサポートする、クラスパスからコンポーネント・クラスを検知し、 それらのビーン定義を自動で登録する機能──に任せることもできます。

コンポーネント・スキャニングの設定情報をコンフィギュレーションに追加することで、アノテーションを付与されたコントローラの自動検知機能が有効になります。次のXML断片に示すように spring-contextスキーマを使用します:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="org.springframework.samples.petclinic.web"/>

    <!-- ... -->

</beans>

17.3.2 @RequestMappingによりリクエストをマッピングする

@RequestMappingアノテーション/appointmentsのようなURLをあるクラスや特定のハンドラ・メソッドマッピングします。典型的なクラス・レベルのアノテーション付与は、特定のリクエスト・パス(もしくはパスのパターン)をフォーム・コントローラにマッピングするものです。この場合、メソッド・レベルのアノテーション付与によって、クラス・レベルのマッピングをより細分化させて、特定のHTTPメソッド(GETやPOSTなど)に絞り込んだり、HTTPリクエスト・パラメータに何かしらの条件付けを行ったりします。

次のコードはPetcareサンプル・アプリケーションから抜粋したもので、Spring MVCアプリケーションにおいて@RequestMappingアノテーションを使用してコントローラを定義しているものです:

@Controller
@RequestMapping("/appointments")
public class AppointmentsController {

    private final AppointmentBook appointmentBook;

    @Autowired
    public AppointmentsController(AppointmentBook appointmentBook) {
        this.appointmentBook = appointmentBook;
    }

    @RequestMapping(method = RequestMethod.GET)
    public Map<String, Appointment> get() {
        return appointmentBook.getAppointmentsForToday();
    }

    @RequestMapping(value="/{day}", method = RequestMethod.GET)
    public Map<String, Appointment> getForDay(@PathVariable @DateTimeFormat(iso=ISO.DATE) Date day, Model model) {
        return appointmentBook.getAppointmentsForDay(day);
    }

    @RequestMapping(value="/new", method = RequestMethod.GET)
    public AppointmentForm getNewForm() {
        return new AppointmentForm();
    }

    @RequestMapping(method = RequestMethod.POST)
    public String add(@Valid AppointmentForm appointment, BindingResult result) {
        if (result.hasErrors()) {
            return "appointments/new";
        }
        appointmentBook.addAppointment(appointment);
        return "redirect:/appointments";
    }
}

この例のなかでは、@RequestMappingが何度も使用されています。最初は型レベル(クラス・レベル)──これによりこのコントローラのハンドラ・メソッドはすべて/appointmentsというパスと関連付けされます。get()メソッドはより明確化された@RequestMappingを持ちます: GETリクエストしか受け付けない、 /appointmentsに対するGETリクエストにはこのメソッドが応えるということです。add()メソッドも同様に明確化され、getNewForm()メソッドはHTTPメソッドだけでなくパス断片にも関連付けられます──appointments/newというパスへのGETリクエストはこのメソッドが応えます。

getForDay()メソッド@RequestMappingの別の使用方法を示します:これはURIテンプレートと呼ばれます(次のセクションで説明します)。

クラス・レベルの@RequestMappingアノテーションは必須ではありません。クラス・レベルのアノテーションが存在しない場合、〔訳注:メソッド・レベルのアノテーションで指定されるパスは〕相対パスではなく絶対パスになります。次のコードはPetClinicサンプル・アプリケーションから抜粋されたもので、 @RequestMappingを使用したマルチ・アクション・コントローラの例です:

@Controller
public class ClinicController {

    private final Clinic clinic;

    @Autowired
    public ClinicController(Clinic clinic) {
        this.clinic = clinic;
    }

    @RequestMapping("/")
    public void welcomeHandler() {
    }

    @RequestMapping("/vets")
    public ModelMap vetsHandler() {
        return new ModelMap(this.clinic.getVets());
    }

}
@ControllerAOPプロキシ

ときによって、コントローラは実行時にAOPプロキシによる修飾を受ける必要があることもあります。例えば、@Transactionalアノテーションをコントローラに直接指定した場合がこれに該当します。この場合、コントローラについてはとくに、クラス・ベースのプロキシを使用されることをおすすめします。一般に、これはコントローラに関してデフォルトの設定です。しかしながら、コントローラが(例えば InitializingBean、*Awareほか)Springコンテキストのコールバックでないインターフェースを実装する必要がある場合、クラス・ベースのプロキシを明示的に設定してやる必要があります。例えば、<tx:annotation-driven /><tx:annotation-driven proxy-target-class="true" />に変更します。

Spring MVC 3.1で導入された@RequestMappingメソッドをサポートするクラス

@RequestMapping メソッドをサポートする新しいクラスとして、Spring 3.1でそれぞれRequestMappingHandlerMappingRequestMappingHandlerAdapterが導入されました。これらのクラスは使用が推奨されているだけでなく、Spring MVC 3.1およびそれ以降で登場した新しい機能を使用するために必須のものとなっています。MVCXML名前空間MVCJavaコードによるコンフィギュレーションではデフォルトでこの2つのクラスが有効になっています。これらを使用しない場合は明示的に設定を記述する必要があります。このセクションでは従来のクラスと新たに導入されたクラスの間におけるいくつかの重要な差異を説明します。

Spring 3.1よりも前には、型レベルとメソッド・レベルのリクエスト・マッピングは2段階に分けて試みられていました──コントローラは最初に DefaultAnnotationHandlerMappingにより選択され、続いて AnnotationMethodHandlerAdapterにより実際に呼び出されるメソッドが絞り込まれるというふうにです。

Spring 3.1の新しいクラスでは、リクエストをどのメソッドが処理すべきかを決める役割はすべて RequestMappingHandlerMappingが負います。コントローラ・メソッドは、型レベルおよびメソッド・レベルの@RequestMappingの情報から導出された各メソッドへのマッピングを伴う、一意のエンドポイントのコレクションと考えることができます。

これによりいくつかの新しい可能性が開かれます。一例だけ挙げると、HandlerInterceptorHandlerExceptionResolverObject型で受け取る引数はHandlerMethod であることが約束されます。それにより〔訳注:ハンドラ・オブジェクトではなく〕まさしくメソッド、そしてそのパラメータとそれに関連付けられたアノテーションについて検査することができるようになります。あるリクエストURLの処理をコントローラごとに分けて記述する必要はなくなるのです。

反対に、もはや不可能になることもいくつかあります:

  • まずSimpleUrlHandlerMappingBeanNameUrlHandlerMapping でコントローラ・オブジェクトを選択し、次にメソッド・ベースの@RequestMapping で絞り込みをかける手法。
  • @RequestMappingが付与されているものの、明示的にパスがマッピングされておらず、他の条件──例えばHTTPメソッドなど──についてはすべて同じ2つのメソッドの曖昧性を回避するためのフォールバック・メカニズムとしてメソッド名に頼る手法。新しいサポート・クラスを使用する場合、@RequestMapping メソッドは一意にマッピングされていなくてはなりません。
  • コントローラのメソッドのうち、リクエストを処理するために他のより適切なものが見つからなかった場合のために(明示的なパス・マッピングを持たない)単一のデフォルト・メソッドを用いる手法。新しいサポート・クラスを使用する場合、適切なメソッドが見つからなければ404エラーが発生します。

上記の機能は従来版のクラスでは引き続きサポートされます。しかしながら、Spring MVC 3.1の新機能を利用するためには、新しいサポート・クラスを利用する必要があります。

URI テンプレート・パターン

@RequestMappingメソッドからURLの特定の断片に平易にアクセスするために、URI テンプレートが利用できます。

URIテンプレートはURIに似通った文字列で、1つかそれ以上の変数名を含んでいるものです。それらの変数に実際に値が割り当てられると、テンプレートはURIそのものになります。URIテンプレートに関して提案されているRFCの中で、URIがどのようにパラメータ化されるかが定義されています。例えば http://www.example.com/users/{userId}というURIテンプレートは、 userId という変数を含んでいます。ここにfredという値が割り当てられると、 http://www.example.com/users/fred というURIになります。

Spring MVCでは、メソッドの引数に@PathVariableアノテーションを付与することで、URIテンプレートの変数に該当する値にアクセスすることができます:

@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(@PathVariable String ownerId, Model model) {
    Owner owner = ownerService.findOwner(ownerId);
    model.addAttribute("owner", owner);
    return "displayOwner";
}

" /owners/{ownerId}"というURLテンプレートは、ownerIdという名前の変数を定義しています。コントローラがリクエストを処理するとき、変数ownerIdには、URIの該当箇所にあたる文字列が設定されます。例えば、/owners/fredというリクエストが来た場合、ownerIdの値はfredです。

[NOTE] @PathVariableアノテーションを処理するため、Spring MVCは名前に基づいてURIテンプレート変数を見つけ出します。次のように名前を明示的に指定することもできます:

@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(@PathVariable("ownerId") String theOwner, Model model) {
    // 実装は省略
}

一方、URIテンプレート変数名がメソッド引数名に一致する場合には、詳細を省略することもできます。コードをデバッグ情報なしでコンパイルするようなことをしない限り〔訳注:Javacコマンドの-gオプションで明示的に指定することで可能〕、Spring MVCメソッド引数名とURIテンプレート変数名とを照合します:

@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(@PathVariable String ownerId, Model model) {
    // 実装は省略
}

メソッド@PathVariableアノテーションをいくつでも持つことができます:

@RequestMapping(value="/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET)
public String findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
    Owner owner = ownerService.findOwner(ownerId);
    Pet pet = owner.getPet(petId);
    model.addAttribute("pet", pet);
    return "displayPet";
}

Map<String, String>型の引数に@PathVariableアノテーションを使用すると、 マップにはすべてのURIテンプレート変数値が格納されます、
URIテンプレートは型とパス〔訳注:というよりメソッド〕のそれぞれのレベルの@RequestMappingアノテーションから組み立てることもできます。これにより、 findPet()メソッド/owners/42/pets/21のようなURLにより起動することが可能になります:

@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {

    @RequestMapping("/pets/{petId}")
    public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
        // implementation omitted
    }

}

@PathVariable 引数には、int、long、Dateその他の「シンプルな型」も使用可能です。Springは自動で適切な型に変換を試み、失敗した場合は TypeMismatchException をスローします。デフォルトでサポートされていないデータ型のパースをサポートするための情報を登録することも可能です。「メソッド引数と型変換」、そして「WebDataBinderの初期化処理をカスタマイズする」というセクションを参照してください。

正規表現を用いたURIテンプレート・パターン

場合によっては、より詳細なかたちでURIテンプレート変数を定義する必要があります。"/spring-web/spring-web-3.0.5.jar"というURLについて考えてみましょう。これをどのように複数の部分に分割していけるでしょうか?
@RequestMappingURIテンプレート変数として正規表現の使用をサポートしています。構文は{varName:regex}で、コロンの前側が変数名、後側が正規表現です。例えば:

@RequestMapping("/spring-web/{symbolicName:[a-z-]}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]}")
    public void handle(@PathVariable String version, @PathVariable String extension) {
        // ...
    }
}
パス・パターン

URIテンプレートに加えて、@RequestMappingアノテーションは、Antスタイルのパス・パターン(例えば、/myPath/*.do)もサポートしています。URIテンプレート変数とAntスタイルのワイルドカードの組み合わせも可能です(例えば、/owners/*/pets/{petId})。

パス・パターンの比較

URLが複数のパターンにマッチした場合、もっとも曖昧性が低いかたちでマッチするパターンを見つけるためにソートが使用されます。

URI変数とワイルドカードがより少ないパターンがより曖昧性が低いものとみなされます。例えば、/hotels/{hotel}/* にはURI変数が1つとワイルドカードが1つ含まれます。このパターンは/hotels/{hotel}/**──URI変数が1つとワイルドカードが2つ含まれる──より曖昧性が低いと判断されます。

2つのパターンが同じカウント数となる場合は、長い方のパターンがより曖昧性が低いものとみなされます〔訳注:次のパラグラフで示されていることですが、ここで言う「長さ」は文字列としての長さではなく、パス断片の数のことです〕。例えば/foo/bar*/foo/*よりも長く、したがってまた曖昧性が低いものと判断されます。

2つのパターンが同じカウント数で同じ長さの場合、ワイルドカードの数が少ないパターンがより曖昧性が低いパターンとみなされます。/hotels/{hotel}/hotels/*より曖昧性が低いと判断されます。

その他いくつか特別ルールがあります:

  • デフォルト・マッピング・パターン/**は他のいかなるパターンよりも曖昧性の高いものとみなされます。 例えばこのパターンに比べれば/api/{a}/{b}/{c}のほうが曖昧性が低いということです。
  • /public/**のような接頭辞パターンは、2連続ワイルドカードを含まない他のいかなるパターンよりも曖昧性が高いものとみなされます。例えば、前述の接頭辞パターンと比べれば/public/path3/{a}/{b}/{c}のほうが曖昧性が低いということです。

完全な情報は AntPathMatcherのなかの AntPatternComparatorの箇所を参照してください。なお、 PathMatcherはカスタマイズ可能です(Spring MVCコンフィギュレーションについて説明するセクションのなかの「パス・マッチング」を参照してください)。

プレースホルダをともなうパス・パターン

@RequestMappingアノテーションで指定するパターンのなかでは、ローカル・プロパティおよび(もしくは)システム・プロパティ、そして環境変数に対応する${…}プレースホルダも使用できます。プレースホルダは、コントローラにマッピングされたパスを、コンフィギュレーションに基づきカスタマイズする場合に便利です。プレースホルダについてより詳細な情報は、 PropertyPlaceholderConfigurerJavadocを参照してください。

接尾辞によるパス・パターン・マッチング

Spring MVCはデフォルトで、接尾辞パターン".*"によるマッチングを自動的に行います。例えば/personというパターンは、暗黙的に/person.*というパターンとして機能します。これにより、/person.pdf/person.xml など、拡張子でコンテント・タイプを示すことができるようになります。一般的な落とし穴として、最後のパス断片がURI変数であるケースがあります。例えば/person/{id}というパターンがマッピングに使用されている場合です。/person/1.jsonというURIに対するリクエストは適切に処理され、結果としてはパス変数idに1が割り当てられ".json"は拡張子とみなされます。しかし/person/joe@email.com のように、idがドットを含んでしかるべきケースでは、結果は期待通りになりません。".com"は明らかにファイル拡張子などではありません。

こうしたURIを適切に処理するには、Spring MVCの設定を変更し、接尾辞パターン・マッチングがコンテント・ネゴシエーションのためにあらかじめ登録されているファイル拡張子にしか働かないようにします。詳しくはまず「コンテント・ネゴシエーション」のセクション(17.16.4)を参照し、続いて「パス・マッチング」のセクション(17.16.9)──どのようにして登録済み接尾辞パターンに対してのみ接尾辞パターン・マッチングが行われるようにするかが説明されている──を参照してください。

マトリクス変数

URI仕様RFC3986では、パス断片のなかに名前-値ペアを含める方法が定義されています。これに関して仕様書では明確な呼び名が使用されていません。Tim Berners-Leeによる古いポストに由来し、人口に膾炙した「マトリクスURI」というより一意性の高い呼び名ではなく、「URIパス・パラメータ」というより一般的な呼び名が使われています。Spring MVCでは前述の方法をマトリクス変数と呼んでいます。
マトリクス変数はパス断片のどこにでも使用できます。それぞれの変数はセミコロンにより区切られます。例えば、"/cars;color=red;year=2012"というように。複数値はカンマ区切りで"color=red,green,blue"と指定するか、もしくは、同じ変数名を繰り返し登場させ"color=red;color=green;color=blue"と指定します。

URLにマトリクス変数を含まれる場合、リクエスト・マッピング・パターンはURIテンプレートで表現されなくてはなりません。URIテンプレートにより、マトリクス変数が存在するかしないか、あるいはまたそれらの並び順はどうなるかといったことにかかわらず、適切にマッチングさせることが可能になります。

以下の例ではマトリクス変数"q"を抽出しています:

// GET /pets/42;q=11;r=22

@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET)
public void findPet(@PathVariable String petId, @MatrixVariable int q) {

    // petId == 42
    // q == 11

}

すべてのパス断片にマトリクス変数が含まれうることから、場合によっては変数がどこに登場すべきかをより明確に指定する必要が出てきます:

// GET /owners/42;q=11/pets/21;q=22

@RequestMapping(value = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)
public void findPet(
        @MatrixVariable(value="q", pathVar="ownerId") int q1,
        @MatrixVariable(value="q", pathVar="petId") int q2) {

    // q1 == 11
    // q2 == 22

}

マトリクス変数の指定を任意にしてデフォルトの値を指定することもできます:

// GET /pets/42

@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET)
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) {

    // q == 1

}

すべてのマトリクス変数をマップに格納させることもできます:

// GET /owners/42;q=11;r=12/pets/21;q=22;s=23

@RequestMapping(value = "/owners/{ownerId}/pets/{petId}", method = RequestMethod.GET)
public void findPet(
        @MatrixVariable Map<String, String> matrixVars,
        @MatrixVariable(pathVar="petId"") Map<String, String> petMatrixVars) {

    // matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
    // petMatrixVars: ["q" : 11, "s" : 23]

}

なお、マトリクス変数の機能を有効にするには、 RequestMappingHandlerMapping removeSemicolonContentプロパティの値をfalseに設定する必要があります。デフォルトではこの値はtrueです。

[NOTE] JavaコードによるコンフィギュレーションMVC名前空間を使用したXMLによるコンフィギュレーションのいずれにおいても、マトリクス変数を有効化するオプションを提供しています。

Javaコードによるコンフィギュレーションを使う場合、「Javaコードによるコンフィギュレーションでより詳細にカスタマイズ」〔Advanced Customizations with MVC Java Config〕のセクションを参照してください。 RequestMappingHandlerMapping を使ったカスタマイズの方法が説明されています。

MVC名前空間を使用したXMLによるコンフィギュレーションを使う場合、<mvc:annotation-driven>要素の enable-matrix-variables 属性にtrueを設定する必要があります。デフォルトではfalseです。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <mvc:annotation-driven enable-matrix-variables="true"/>

</beans>
受け入れ可能な〔consumable〕メディア・タイプ

マッピングに受け入れ可能なメディア・タイプのリストによる制限を付け加えることもできます。これにより、Content-Typeリクエスト・ヘッダの内容が適合したリクエストのみがハンドラにマッチするようになります。例えば:

@Controller
@RequestMapping(value = "/pets", method = RequestMethod.POST, consumes="application/json")
public void addPet(@RequestBody Pet pet, Model model) {
    // implementation omitted
}

受け入れ可能なメディア・タイプの表記には否定形も使用できます。例えば!text/plain のように記述すると、Content-Typeの値が text/plain でないすべてのリクエストがマッチするようになります。

[NOTE] この条件は型レベルとメソッド・レベルの双方でサポートされています。ただし他のほとんどの条件とちがい、型レベルで指定した条件は、メソッド・レベルで指定した条件により拡張されるのではなく、上書きされます。

生成可能な〔producible〕メディア・タイプ

マッピングに生成可能なメディア・タイプのリストによる制限を付け加えることもできます。これにより、Acceptリクエスト・ヘッダの値のうちのいずれかが適合したハンドラがマッチするようになります。加えて、レスポンスに使用される実際のコンテント・タイプもこの生成可能な条件で指定したものが使用されます。例えば:

@Controller
@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET, produces="application/json")
@ResponseBody
public Pet getPet(@PathVariable String petId, Model model) {
    // implementation omitted
}

受け入れ可能なメディア・タイプの場合と同じで、生成可能なメディア・タイプの表記でも否定形が使用できます。例えば!text/plain のように記述すると、Accept の値に text/plain が含まれないすべてのリクエストがマッチするようになります。

[NOTE] この条件は型レベルとメソッド・レベルの双方でサポートされています。ただし他のほとんどの条件とちがい、型レベルで指定した条件は、メソッド・レベルで指定した条件により拡張されるのではなく、上書きされます。

リクエスト・パラメータとリクエスト・ヘッダー

リクエスト・パラメータを条件にして──"myParam""!myParam"、あるいは "myParam=myValue"のように──リクエスト・マッチングを絞り込むことができます。はじめの2つはリクエスト・パラメータが「存在する」あるいは「存在しない」ことを条件とするもので、3つめはパラメータが特定の値であることを条件とするものです。リクエスト・パラメータ値を条件とするときの例を次に示します:

@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {

    @RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET, params="myParam=myValue")
    public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
        // 実装は省略
    }

}

同様に、リクエスト・ヘッダが「存在する」あるいは「存在しない」ことを条件としたり、リクエスト・ヘッダ値が特定の値であることを条件としたりすることができます:

@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {

    @RequestMapping(value = "/pets", method = RequestMethod.GET, headers="myHeader=myValue")
    public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
        // 実装は省略
    }

}


[NOTE] Content-Typeヘッダと Accept ヘッダに関しては、〔訳注:この節で解説したリクエスト・ヘッダ条件を使用して〕ワイルドカード表記も使用できますが(例えば"content-type=text/*"は、"text/plain""text/html"にマッチします)、受け入れ可能なメディア・タイプ条件や生成可能なメディア・タイプ条件の使用が推奨されています。2つのメディア・タイプ条件は、〔訳注:それぞれのヘッダを処理するという〕目的に特化した機能の提供を意図して用意されています。

          * * *

続きは次の投稿で公開予定です。

原典は、Springフレームワーク(本体)のリファレンス・マニュアルである"Spring Framework Reference Documentation"の第5部"The Web"の第17章"Web MVC framework"の第3節(バージョン4.1.1.RELEASE現在)です。


Spring関連の訳出記事まとめ - M12i.