kagamihogeの日記

kagamihogeの日記です。

Difference between @RestController and @Controller Annotation in Spring MVC and RESTをテキトーに訳した

http://javarevisited.blogspot.jp/2017/08/difference-between-restcontroller-and-controller-annotations-spring-mvc-rest.html を読んだ。実はspringよく知らずに使っている。

Difference between @RestController and @Controller Annotation in Spring MVC and REST

Spring MVC@RestControllerアノテーション@Controller@ResponseBodyを組み合わせたものです。子のアノテーションはSpring 4.0で追加されたもので、RESTfulなWebサービスの開発をしやすくするためのものです。REST web servicesを既に知っているのであれば、webアプリケーションとREST APIの違いについても知っているかと思います。前者のwebアプリケーションのレスポンスは基本的にはビュー(HTML + CSS + JavaScript)である一方、後者のREST APIJSONXML形式のデータのみ返します。これらの相違点は@Controller@RestControllerでも同様です。@Controllerの役割はモデルオブジェクトのMapの生成とビューの参照で、@RestControllerは単にオブジェクトを返し、オブジェクトデータはHTTPレスポンスにJSONXML形式で直接書き込まれます。

これまで通りのやり方である@Controller@ResponseBodyで同じことが出来ますが、この動作はRESTful Webサービスのデフォルトの振る舞いです。そのため、Springは@Controller@ResponseBodyの振る舞いを組み合わせた@RestControllerを導入しました。

つまり、以下の二つのコードはSpring MVCでは等価です。

@Controller
@ResponseBody
public class MVCController { 
   .. your logic
}
@RestController
public class RestFulController { 
  .... your logic
}

当然ながら、アノテーションを2個よりも1個の方が好まれます。また、@RestControllerの方が意図が明瞭です。

What are @Controller and @RestController in Spring?

Springフレームワークでは、Controllerのクラスはビューで表示するデータをモデルMapを構築すると共に正しいビューを選択する役割を担います。なお、@ResponseBodyアノテーションでレスポンスのストリームに直接書き込んでリクエストを完了することも可能です。

レスポンスストリームに直接書き込む動作はRESTful Webサービス呼び出しに対する責務にマッチしており、この場合は以前のエントリhow Spring MVC works internallyで述べたようにビューを返す代わりにデータを返したいからです。

Spring 3もしくはSpring 3.1などSpring 4以前でRESTful Webサービスを開発していた場合、RESTfulなレスポンスの生成に@Controller@ResponseBodyの組み合わせを使用していたかと思われます。Springの開発者はこの課題を認識していたので@RestControllerを作成しました。

よって@Controller@RestponseBodyを使う必要は無くなり、同様なことを実現するには@RestControllerを使えば良くなりました。つまり、このアノテーション@Controler@Responseボディの振る舞いを一つにまとめた簡易化コントローラー、ということです。

SpringのRESTFul Webサービスの高度なテクニックに関心があればEugen Paraschiv氏のREST with Spring Master classを購入してみてください。

Difference between @RestController and @Controller in Spring

@RestController@Controlerアノテーションを既に使ったことがあるのであれば、両者の違いを見ておくと良いです。採用面接の点ではなくSpring CoreおよびSpring Web Application developer Certificationの点で重要です。Spring certificationsを受ける場合、これらの微妙な違いを知っておくと良いです。また、フリーのSpringテストで試験形式と問題のレベルを見ておくのも良いでしょう。

ともあれ相違点の話に戻ると、両アノテーションには大きな違いがあります。

1.@ControllerはSpring MVCのContollerとしてクラスを作成するための一般的なアノテーションですが、@RestControllerRESTFul web servicesのための専用コントローラーで@Controller + @ResponseBodyと等価です。

2.@RestControllerは比較的新しいもので、Spring 4.0から追加されたものですが@ControllerはSpringがアノテーションをサポートし始めた古くから存在するもので、正式にはSpring 2.5バージョンから追加されています。

3.@Controllerはそのクラスが"Controller"、例えばwebコントローラー、であることを示す一方、@RestControllerはそのクラスが、@RequestMappingのメソッドがデフォルトで@ResponseBodyを返すと想定されるコントローラ、つまりREST API、を示します。

4.@Controller@Componentを専用化したもので、@RestController@Controllerを専用化したものです。実際には以下のように@Controller@ResponseBodyによる簡易化コントローラーとなっています。

@Target(value=TYPE)
@Retention(value=RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController

@Controllerの宣言は以下のようになっています。

@Target(value=TYPE)
@Retention(value=RUNTIME)
@Documented
@Component
public @interface Controller

5.Spring MVC@Controler@RestCotrollerの主要な相違の一つは、クラスに@RestControllerを付与するとそのすべてのメソッドはビューではなくドメインオブジェクトが書き込まれます*1。Springベースのアプリケーションで@RestControllerを使う方法のより詳しい方法についてはBryan Hassen氏のIntroduction to Spring MVC 4を参照してください。

6.@Controler@RestCotrollerのもう一つの違いは、以下のように@RestControllerをクラスに付与する場合はすべてのハンドラメソッドに@ResponseBodyを付ける必要が無い点です。

with @RestControler

@RestController
public class Book{

@RequestMapping(value={"/book"})
public Book getBook(){
//...
return book;
}
}

without @RestController

@Controller
public class Book{

@RequestMapping(value={"/book"})
@ResponseBody
public Book getBook(){
//...
return book;
}
}

RESTful responseを作成するのにSpring MVC@Controllerを使うと分かるのですが、個々のメソッドに@ResponseBodyを付ける必要があり、@RestControllerではその必要がありません。読みやすさは元よりキーを押す回数も減らせます。

SpringBoot@RestControllerで作成したHelloWorldサンプルは以下になります。

画像:http://javarevisited.blogspot.jp/2017/08/difference-between-restcontroller-and-controller-annotations-spring-mvc-rest.html#axzz4spOd15rP

以上がSpring MVC@Controller@RestControllerの違いになります。@RestControllerは単に@Controllerと@ResponseBodyを組み合わせたものです。

SpringフレームワークでRESTfulなwebサービスを作りやすくする意図で、Spring 4でこのアノテーションは追加されました。リクエストのMIMEタイプに応じてレスポンスをJSONXMLに直接変換します。

このため、 RESTful Web Servicesを作るには@Controller@ResponseBodyを付けるよりも@RestControllerを使う方が良いです。

SpringとSpring SecurityフレームワークでRESTful Web Servicesを開発する場合の詳細を学ぶには、Eugen Paraschiv氏のREST with Spring Coaching classの購入を推奨します。EugenはJavaでのセキュアなRESTful webサービスと実際の開発経験を持っているので、この教材は彼の経験から得られた知識を学ぶには良い機会となっています。

*1: then every method is written a domain object instead of a view. REST APIとしてメソッドを公開すると、役割が変わってくるよーという意味合いと思われるが、なんかヘンな訳。

What does the InternalResourceViewResolver do in Spring MVC?をテキトーに訳した

http://javarevisited.blogspot.jp/2017/08/what-does-internalresourceviewresolver-do-in-spring-mvc.html を読んだ。

What does the InternalResourceViewResolver do in Spring MVC?

InternalResourceViewResolverはSpring MVCフレームワークViewResolverの実装で、"hello"などの論理的なビュー名を、ServletやWEB-INFフォルダー内に配置されるjspファイルなどの内部的な物理リソースへの解決を担当します。このクラスはUrlBasedViewResolverのサブクラスで、Springコントローラーが返す論理的なビュー名を実際の物理的なビューへ変換するのに、"prefix"と"suffix"を使います。たとえば、ユーザがURL/homeにアクセスしてHomeControllerが"home"を返すとすると、DispatcherServletはInternalResourceViewResolverに問い合わせて、webアプリケーションが必要とする実際の物理的なビューを参照するためにprefixとsuffixを使用します。たとえば、prefixが"/WEB-INF/views/"でsuffixが".jsp"の場合、"home"はInternalResourceViewResolverによって"/WEB-INF/home.jsp"に解決されます。

このため、WEB-INFディレクトリ内にJSPファイルを配置する場合、それらのJSPファイルに直接アクセス(例:URLの直接入力)できなくするのが望ましいです。そうする場合、コントローラーだけがそれらのファイルにアクセス可能です。

How to configure InternalResorveViewResolver in Spring MVC

以下に示すように、Java ConfigurationとXML configurationのどちらかでViewResolver を設定できます。

Configuring ViewResolver using XML in Spring

以下はSpringでview resolverを設定するXMLの例で、XMLベースでSpringプロジェクトを動かしている場合には以下のようにします。

<bean id="viewResolver"
    class="org.springframework.web.servlet.view.InternalResourceViewResolver"
    prefix="/WEB-INF/" suffix=".jsp" /> 

Configuring ViewResolver using Java Configuration

Spring 3.0以降はXMLではなくJavaでview resolverを設定できます。internal resource view resolverを設定するには以下のようにします。

@Bean
  public ViewResolver viewResolver() {
    InternalResourceViewResolver irv = new InternalResourceViewResolver();
    irv.setPrefix("/WEB-INF/");
    irv.setSuffix(".jsp");

    return irv;

  }

XMLでもJavaでもSpringでinternal resource view resolverを設定する方法は簡単です。

Important points about InteralResourceViewResolver in Spring MVC

Spring MVCフレームワークの便利なこのクラスについて、以下に知っておいたほうが良い点を述べます。springプロジェクトの処理の流れを知るのに役立つかと思います。

1. 複数のViewResolverをチェーンさせる場合、InternalResourceViewResolverは常に最後にする必要があり、これはビュー名を解決しようとするとき基底のリソースが本当に存在するかどうかに関係無く解決が行われるためです。

2. Spring MVCフレームワークのフロントコントローラDispatcherServletのデフォルトview resolverはInternalResourceViewResolverhです。

3. デフォルトでは、InternalResourceViewResolverInternalResourceView(つまりServletJSP)を返しますが、以下のようにviewClass属性でJstlViewを返すように設定できます。

/**
   * Sets the default setViewClass view class to requiredViewClass: by default
   * InternalResourceView, or JstlView if the JSTL API is present.
   */
  public InternalResourceViewResolver() {
    Class viewClass = requiredViewClass();
    if (viewClass.equals(InternalResourceView.class) && jstlPresent) {
      viewClass = JstlView.class;
    }
    setViewClass(viewClass);
  }

  /**
   * このresolverはInternalResourceViewが必要。
   */
  @Override
  protected Class requiredViewClass() {
    return InternalResourceView.class;
  }

JstlViewの利点はJSTL tagsで、これはLocaleとSpringに設定したメッセージリソースを取得できます。表示メッセージのフォーマットにJSTLタグを使う場合には特に重要です。

JSTLのフォーマットタグは通貨や日付などをロケール固有のフォーマットで適切に行うにはLocaleが必要です。このメッセージタグは、Springのメッセージソースと、HTMLのレンダリングをするのに適切なメッセージ選択を行うためのLocaleを使用します。JstlViewクラスの詳細についてはSpring in Action by Craig Wallsを参照してください。

4. InteralResourceViewResolverSpring frameworkが提供するview resolerのうちの一つで、場合によっては以下のクラスのほうが便利な場合があります。

  • BeanNameViewResolver - Springのアプリケーションコンテキスト内のbeanで、ビュー名とbean IDが同一かどうか、でviewを解決します。たとえば、id = "home"のbeanがありコントローラーが論理的なビュー名"home"を返す場合、BeanNameViewResolverによる解決はこのbeanになります。
  • FreeMarkerViewResolver - FreeMarkerのテンプレートとしてビューを解決します。
  • JasperReportsViewResolver - JasperReportの定義としてビューを解決します。
  • XsltViewResolver - XSLT変換の結果としてレンダリングするためのビューを解決します。

Springのview resolverのそれぞれの種類について知るにはBryan Hassen氏のIntroduction to Spring MVC 4を参照してください。

5. Spring MVCでViewResolverを使う最大のメリットは、ビューのレンダリングとコントローラーのリクエストハンドリングを切り離せる点です。つまり、コントローラーはビューのレンダリングに使われる技術について知らなくてもよい、ということです。

返すのは論理的なビュー名だけで、この名前でJSP, FreeMarker template, Apache tilesなどのビュー技術を解決します。また、論理的なビュー名が同一であればコントローラーを変更することなくビューレイヤを変更できることも意味します。

以上が、Spring MVCでInternalResourceViewResolverがが何をしているか、もしくはInternalResourceViewResolverの役割、の解説になります。Spring MVCの便利なこのクラスは、Java Springの使用者は知っておいたほうが良いでしょう。Spring MVCのビュー解決の考え方は、Springの採用面接やSpring certificationの点でも重要です。Spring certificationを受ける場合、Spring MVCのビュー解決の考え方の知識を確認するのにDavid MayerのSpring Mock examで公開されている設問を見ておくことを推奨します。

WildFly 10のCache-Controlヘッダ変更

standalone.xmlの以下の箇所を編集する。

        <subsystem xmlns="urn:jboss:domain:undertow:3.1">
            <buffer-cache name="default"/>
            <server name="default-server">
                <http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/>
                <https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true"/>
                <host name="default-host" alias="localhost">
                    <location name="/" handler="welcome-content"/>
                    <filter-ref name="server-header"/>
                    <filter-ref name="x-powered-by-header"/>
                    <filter-ref name="cache-control" predicate="path-suffix['.html']"/><!-- ここ(predicateの中は適宜編集)と -->
                </host>
            </server>
            <servlet-container name="default">
                <jsp-config/>
                <websockets/>
            </servlet-container>
            <handlers>
                <file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
            </handlers>
            <filters>
                <response-header name="server-header" header-name="Server" header-value="WildFly/10"/>
                <response-header name="x-powered-by-header" header-name="X-Powered-By" header-value="Undertow/1"/>
                <response-header name="cache-control" header-name="Cache-Control" header-value="no-cache"/><!-- これを追加 -->
            </filters>
        </subsystem>

個々のURLで任意のヘッダーを返すには、素朴にやるならHttpServletResponse#setHeaderする。

   @RequestMapping("/i.html")
    public String i2(HttpServletResponse response) {
        response.setHeader("Cache-Control", "asdasdf");
        return index();
    }

参考URL