#navi_header|Java| JAX-RS 2.0 (JSR 339)の使い心地について調べたのでメモ。 - https://jax-rs-spec.java.net/ - https://jcp.org/en/jsr/detail?id=339 - http://ja.wikipedia.org/wiki/JAX-RS 実装: - Jersey -- https://jersey.java.net/ --- GlassFish プロジェクトでも使われてる、Sunマイクロシステムズ時代からのJAX-RSのリファレンス実装。ライセンスはCDDLv1.1とGPLv2のデュアルライセンス。 - Apache CXF -- Index -- http://cxf.apache.org/ --- JAX-RSだけでなく、JAX-WSも含めたWebサービスの実装。ライセンスは当然ASL2.0 - RESTEasy - JBoss Community -- http://resteasy.jboss.org/ --- JBoss中心で実装が進んだJAX-RS実装。ライセンスはASL2.0 #more|| #outline|| ---- * 雑多なトピック JAX-RSを一般的なWebアプリケーションのためのフレームワークとしてはどう評価されているのか? - JAX-RSはHTML Webアプリケーションを開発するのに充分なフレームワークであるか? - AOEの日記 -- http://d.hatena.ne.jp/aoe-tk/20130317/1363527825 - JAX-RSとかの話 — 裏紙 -- http://backpaper0.github.io/2013/05/02/jaxrs.html 結論:まずます良さげ。 JAX-RS関連調べてて気になったトピック - Bean Validation: Bean Validation -- http://beanvalidation.org/ --- JAX-RSの実装でもvalidationに使われてる、アノテーションベースのvalidationの仕様と実装 - 私のBeanValidationの使い方(Java EE Advent Calendar 2013) — 裏紙 -- http://backpaper0.github.io/2013/12/03/javaee_advent_calendar_2013.html - JAX-RSでパラメータの受け取り方をいろいろ試す — 裏紙 -- http://backpaper0.github.io/2013/07/17/jaxrs_parameter.html --- サンプルとして参考になります。 - Oracle Blogs 日本語のまとめ: [Java] Java EE 7 and JAX-RS 2.0 -- http://orablogs-jp.blogspot.jp/2013/04/java-ee-7-and-jax-rs-20.html --- JavaEE7とJAX-RS 2.0の紹介 - Project Grizzly - Http Server Framework Overview -- https://grizzly.java.net/httpserverframework.html --- HTTP ServerとしてのGrizzlyの提供するフレームワーク概要。セッションとかフィルタなど、ちゃんと備えてる。 - Grizzly 2.0: HttpServer API. Implementing simple HTTP server (Mytec) -- https://blogs.oracle.com/oleksiys/entry/grizzly_2_0_httpserver_api --- OracleのBlog記事。 * JAX-RSでのユーザ認証について Servletコンテナで提供しているRoleベースの認証機能を使う。JAX-RSでは"@Context"アノテーションでSecurity Contextにアクセスできるよう定められているため、Servletコンテナ側でBasic認証など設定していれば、FilterなどのProviderや、リソースから"@Context"経由で参照し、処理出来る。 - A simple JAX-RS security context example in GlassFish | IT Business Tonic -- http://www.butonic.de/2010/06/18/a-simple-jax-rs-security-context-example-in-glassfish/ - Jersey (JAX-RS) のアクセス制御 - あめだま -- http://d.hatena.ne.jp/momijiame/20120215/1329317290 - forcing the Jersey Grizzly server to authenicate - Stack Overflow -- http://stackoverflow.com/questions/15980658/forcing-the-jersey-grizzly-server-to-authenicate --- Grizzlyでのユーザ認証でSecurityContextをどう使うかについての相談 - Jersey (JAX-RS) implements a HTTP Basic Auth decoder | Simple API -- http://simplapi.wordpress.com/2013/01/24/jersey-jax-rs-implements-a-http-basic-auth-decoder/ --- JAX RSで、Filterを使って独自にBasic認証を実装するサンプル →結論は↓のトピックに持ち越し * RESTfulなAPIサービスと、HTTPセッションについて - Jersey - In JAX-RS: Jersey: API model, how do you access HTTP session variables -- http://jersey.576304.n2.nabble.com/In-JAX-RS-Jersey-API-model-how-do-you-access-HTTP-session-variables-td2953702.html --- そもそもRESTでセッション使うものなんだっけ?というメールスレッド。基本的にはRESTのステートレス性を損なうので使わない方が良いのだけれど、使うならこうしてね、というアドバイスの返信がある。 以下、RESTfulなAPIサービスで認証をどう実装すればよいのか、Stack Overflowでも賑わってました: - web services - How Can I Retrieve The Session Id from a Jax RS Webservice? - Stack Overflow -- http://stackoverflow.com/questions/4819837/how-can-i-retrieve-the-session-id-from-a-jax-rs-webservice --- JAX RSでServletRequestからセッションIDを取得する方法の例示。 - jersey - HttpSession is null in RESTful web service? - Stack Overflow -- http://stackoverflow.com/questions/13882935/httpsession-is-null-in-restful-web-service --- Tomcat7でJersey使った時に、HttpServletRequest#getSession(boolean)でちょっとハマった事例。 - Authentication for REST web services - Stack Overflow -- http://stackoverflow.com/questions/19721403/authentication-for-rest-web-services - methods - How do I authenticate user in REST web service? - Stack Overflow -- http://stackoverflow.com/questions/5528272/how-do-i-authenticate-user-in-rest-web-service →結論は↓のトピックに再度持ち越し * RESTとステートレス性についてのそもそもの話 - http://en.wikipedia.org/wiki/REST - http://www.ics.uci.edu/~taylor/documents/2002-REST-TOIT.pdf - REST について調べたまとめ - Slow Dance -- http://d.hatena.ne.jp/LukeSilvia/20091025/p1 RESTではスケーラビリティ等を考慮して、ステートレスにしよう、という思想がある。そこで、CookieによるセッションIDで認証状態を管理してしまうと、サーバ側でのスケーラビリティで色々制約や工夫の必要が生じてしまうので、なるべくクライアント側だけでステートを持たせようね、という話があるようです。 - Representational State Transfer (REST) : 5.1.3 Stateless -- http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_3 - Experience and Evaluation : 6.3.4.2 Cookies -- http://www.ics.uci.edu/~fielding/pubs/dissertation/evaluation.htm#sec_6_3_4_2 - RESTアンチパターン -- http://www.infoq.com/jp/articles/rest-anti-patterns - Learn REST: A Tutorial: 14.3. How do I handle authentication in REST? -- http://rest.elkstein.org/2008/01/how-do-i-handle-authentication-in-rest.html 結論・・・というかとりあえずの検討結果: - ステートレス性を犠牲にしてスケーラビリティで苦労するのを受け入れた上で、普通にServletのCookieによるセッション管理機能をフィルタとかで組み込む方向性はアリだと思いました。 - Ajaxを使ったRIAで、AjaxだけBasic認証というのはかなり辛いと思いますので、OAuth2.0系を使ってtokenをクライアント側で管理してGET以外のパラメータでやりとりするのもありかなと。GETでやりとりしてしまうとプロキシやサーバログとかに残ってセキュリティ上問題になるので、GET禁止・・・となるとまたRESTの特性の一部が失われますが。 - Railsがやってるように、Client側のCookieの中にセッション状態を詰め込むのもアリだと思いますが、これはもう言語やプラットフォームごとにこのアプローチの得手不得手が出てきてしまいます。うかつに独自実装すると、セキュリティ上穴が出来てしまわないか怖いですし・・・。 - どちらにしても、原理主義でいくか、現場主義でいくか、バランス取らないとやってけないと思いました。 * JAX-RS 2.0のフィルタ機能の参考 - Chapter 9. Filters and Interceptors -- https://jersey.java.net/documentation/latest/filters-and-interceptors.html - JAX-RS のリファレンス実装 Jersey のフィルタを使う方法 - あめだま -- http://d.hatena.ne.jp/momijiame/20111115/1321372807 - JAX-RS のリファレンス実装 Jersey のフィルタを使う方法 その2 - あめだま -- http://d.hatena.ne.jp/momijiame/20111116/1321455103 - WebAPIのステートレスなCSRF対策 - あめだま -- http://d.hatena.ne.jp/momijiame/20111204/1323000833 --- ・・・これ、XHR2 で他のOriginからもXHR呼べるようになったので、このCSRF対策破綻してるような・・・。 フィルタ中から、ContainerRequestContext#setProperty()経由でセットしたオブジェクトを、リソースクラスから読み出せるか? - java - JBoss / Resteasy how to prepopulate objects using a filter before the exposed API? - Stack Overflow -- http://stackoverflow.com/questions/20239025/jboss-resteasy-how-to-prepopulate-objects-using-a-filter-before-the-exposed-ap - java - How I can add parameter to request from jersey filter request (ContainerRequestFilter) - Stack Overflow -- http://stackoverflow.com/questions/18876053/how-i-can-add-parameter-to-request-from-jersey-filter-request-containerrequestf - Read request attribute in a Jersey ContainerRequestFilter - Stack Overflow -- http://stackoverflow.com/questions/19691753/read-request-attribute-in-a-jersey-containerrequestfilter 結論:JAX-RS 2.0としての仕様ではそうしたアクセス用APIは(多分)提供されてない(っぽい)。 一応、Servletアプリ上で動かす場合はContainerRequestContext#setProperty()/getProperty()は ServletRequest の Attribute と連動しているよう定められているため、リソースクラスに "@Context" で HttpServletRequest をインジェクションして、そこから取り出す方法はある。 なお、JAX-RSのApplicationクラスが提供しているgetProperties()は、JAX-RSアプリ全体のconfigurationだったり、Servlet実装の場合はと連動してたりするため、フィルタ機能とは関係ない。ただし、アプリ全体の設定値を取得する際には活用できそう。 * Jersey使った時に、アプリ全体の初期化処理用のフックとかあるかな?(Servletのinitみたいな) - java - Initialize database on Jersey webapp startup - Stack Overflow -- http://stackoverflow.com/questions/12875682/initialize-database-on-jersey-webapp-startup - Jersey app-run initialization code on startup to initialize application - Stack Overflow -- http://stackoverflow.com/questions/7424465/jersey-app-run-initialization-code-on-startup-to-initialize-application - Jersey - Servlet.Init() For Jersey REST Service -- http://jersey.576304.n2.nabble.com/Servlet-Init-For-Jersey-REST-Service-td6507144.html 結論:仕様としては、無い。 ただし、例えばFilterはライフサイクル上、ひとつのアプリで1インスタンスだけなので、コンストラクタでグローバルな設定を初期化するのはアリだろう。 また、Servletコンテナ上で動かすのであれば、普通にServletのinit中で、Singletonで初期化するのもアリ。Servlet中のClassLoader上で動作するのであれば、JAX-RSアプリ内からも参照できるはず。Singletonを上手く使うのもちょっと色々工夫がいるだろうけど・・・。 あとはSpringやGuiceなどのDIコンテナと上手く組み合わせて工夫するとか。 * エラー・例外のカスタマイズ - 6.3. WebApplicationException and Mapping Exceptions to Responses -- https://jersey.java.net/documentation/latest/representations.html - JAX-RS アプリケーションの 404 Not Found のカスタマイズ (リソースが見つからない場合) - ひだまりソケットは壊れない -- http://vividcode.hatenablog.com/entry/java/jax-rs/not-found-customize - java - JAX-RS / Jersey how to customize error handling? - Stack Overflow -- http://stackoverflow.com/questions/583973/jax-rs-jersey-how-to-customize-error-handling リソースクラスの処理内で発生した例外についてはこんな感じかな? + 自力でtry-catchした後に、カスタマイズした専用のWebApplicationException派生の例外クラスをthrowする。 + カスタマイズしたException派生の例外クラスをthrowし、それに対応するExceptionMapperをProviderとして用意しておく。 + カスタマイズしたException派生の例外クラスをthrowし、実行環境(Servletコンテナなど)のデフォルトの例外処理に委譲する。 また、JAX-RS側のデフォルトの404例外などをカスタマイズしたい場合は、内部的に以下のような例外が定められていてthrowされるので、それに対応するExceptionMapperを自分たちで準備しておけば良いっぽい。(単にJavaDocからそれっぽいのを引っ張ってきてるだけのため、本当にそうなのかは不明。) - NoContentException - NotAcceptableException - NotAllowedException - NotAuthorizedException - NotFoundException - NotSupportedException こういうのがあるのを知っておけば、カスタム例外を作る手間が省けて、JAX-RS側で用意してくれてるこれらの例外で済ませられるケースもあるかも? * レスポンスHTTPヘッダーやボディのカスタマイズ - Chapter 6. Representations and Responses -- https://jersey.java.net/documentation/latest/representations.html - jax rs - Is it possible to control the filename for a Response from a Jersey Rest service? - Stack Overflow -- http://stackoverflow.com/questions/5318132/is-it-possible-to-control-the-filename-for-a-response-from-a-jersey-rest-service - my kingdom for a smile :-): How to return a Location header from a Jersey REST service -- http://usna86-techbits.blogspot.jp/2013/02/how-to-return-location-header-from.html 結論としては、HTTPレスポンスのカスタマイズ性は非常に自由度が高い状態にある。 まず、Response#status()でステータスコードを指定してResponseBuilderのインスタンスを取得する。 ResponseBuilderには、以下のようにレスポンスをカスタマイズできるメソッドが公開されている。 - entity() : byte[]やStringやObjectなど、レスポンスBODYを設定出来る。 - header() : レスポンスヘッダをカスタマイズ出来る。 - type() : Content-Typeヘッダをカスタマイズできる。"text/html"や"application/octet-stream"など頻用される値についてはMediaTYpeのstaticインスタンスとして定義済みなので、いちいち正確な綴りを思い出す必要がない。"charset"指定も、自然な形でメソッドとして統合されている。 - location() : 基本的にリダイレクト系は、Response#seeOther(URI), temporaryRedirect(URI), created(URI) でステータスコードも一緒に設定してくれるが、フルカスタマイズで個別にLocationヘッダーを設定したい場合はこちらのメソッドが使える。 - encoding() : ガラケーサイト向けにSJISで出力したい、という場合に、entity()にはJava世界の文字列をそのままStringで渡して、こちらのencoding()で実際に出力するときのエンコーディングを指定する。Content-Typeのcharsetが自動的に連動してくれるかは不明なので、自分でtype()で指定しておくのが無難か。 上記メソッドは、いずれもそのメソッドで更新されたResponseBuilder()を返してくるので、メソッドコールのチェインをつなげていく格好になり、最後に"build()"メソッドを呼べば、最終的なResponseクラスのインスタンスが返される。これを単純にreturnすればよい。 ※"@Context"でHttpServletResponseをインジェクとしてもらって、それも合わせて操作した場合に、何か衝突など発生しないか、については未検証。セッション管理系のCookie操作とか大丈夫かな? #navi_footer|Java|