Web Service Servlet
JSONICには、JSON-RPC1.0によるRPCモードとRestfull APIによるRESTモードの2 Way Web Service機能が用意されています。
設定方法
JSON Web Service Servletの設定は簡単です。web.xmlにWebServiceServletを指定し、パスとClassのマッピングなどの設定を行うだけです。
<servlet> <servlet-name>json-ws</servlet-name> <servlet-class>net.arnx.jsonic.web.WebServiceServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value> { "debug": true, "encoding": "UTF-8", "expire": true, "mappings": { "/{package}/{class}.{ext}": "sample.web.${package}.service.${class}Service", "/{class}.{ext}": "sample.${class}Service" } } </param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>json-ws</servlet-name> <url-pattern>*.json</url-pattern> </servlet-mapping>
configで設定できる値は次の通りです。
キー | 値型 | 説明 |
---|---|---|
container | net.arnx.jsonic.web.Container | クラスのインスタンスを取得するためのコンテナを設定します。デフォルトは、net.arnx.jsonic.web.Container です。 |
encoding | java.lang.String | Request/Responseの文字エンコーディングを設定します。デフォルトはUTF-8です。 |
expire | java.lang.Boolean | クライアントキャッシュを抑制するHTTPヘッダを出力します(Cache-Control: no-cache, Pragma: no-cache, Expires: Tue, 29 Feb 2000 12:00:00 GMT )。デフォルトはtrueです。 |
debug | java.lang.Boolean | デバッグモードの有効/無効を切り替えます。デフォルトはfalseです。 |
mappings | java.util.Map<String, String> | URLパスとクラスのマッピングを行います。
パス中の{name} で囲まれた部分はクラス名の${name} に置換されます(※1)。
なお、RESTモードの場合、利用されなかった変数は引数としてメソッドに引き渡されます(RPCモードの場合は設定されません。詳しくはRESTモードの説明をご覧ください)。また、{name:regex} と記載することで、JSR-311同様パターンを設定することができます。 |
definitions | java.util.Map<String, Pattern> | mappings中の変数の定義を正規表現で設定します。設定されない場合は[^/().]+ が設定されたものと扱われます。 |
init | java.lang.String | 処理の実行前に呼び出されるメソッド名を設定します。デフォルトは"init" です。 |
destroy | java.lang.String | 処理の実行後に呼び出されるメソッド名を設定します。デフォルトは"destroy" です。 |
processor | net.arnx.jsonic.JSON | 処理に使用するJSONクラスを設定します。デフォルトは、net.arnx.jsonic.web.WebServiceServlet.JSON です。 |
RPCモード
JSON-RPCは、JSONを使ったシンプルなRemote Procedure Callプロトコルです。JSON Web Service Servletでは、class変数の値が"rpc"
であった場合、RPCモードとなります。
RPCモードでは、対象のパスに対し次のようなJSONをPOSTすることで、対象クラスのメソッドを呼び出すことができます(GET/PUT/DELETEは無効です)。paramsに指定された配列の値はメソッドの引数に指定された型に従い自動的に変換されます。なお、クラス名はUpperCamel、メソッド名はLowerCamelに自動的に変換されます。そして、実行後、戻り値がJSONに変換されクライアントに返されます。
{ "method": "class.method", "params": [ arg1, arg2, ... ], "id": request_id }
class, methodにはそれぞれ変数の値、argNにはメソッドの引数を設定してください。requesst_idには送受信の同期確認用のキーとしてnull以外の任意の値を設定してください(HTTPでは、送信と受信は同期処理ですのでほとんど意味はありませんが、省略すると通知(Notification)モードとなりレスポンスのメッセージボディが返されませんので必ず値を指定してください)。
例えば、mappingsに "/[package]/[class].[ext]": "boo.${package}.${class}Service" という指定があった場合、 /foo/woo/rpc.jsonというパスに次のJSONがPOSTすると、boo.foo.woo.CalcServiceクラスのint plus(int a, int b)のようなメソッドが呼び出されます。
{ "method": "calc.plus", "params": [1,2], "id": 1 }
この時、レスポンスのメッセージボディとしては次のような結果が返されます。
{ "result": 3, "error": null, "id": 1 }
RPCモードでエラーが発生した場合にはレスポンスのメッセージボディでクライアントに通知されます。ステータスコードは、エラーの有無に関わらず200 OKが返されます。
errorの値には code, message, dataの三つのキーを持つJSON objectが設定されます。codeとmessageについては次表を参照してください。dataには投げられた例外のプロパティがセットされます(ただし、Throwableクラスのプロパティは除外されます)。
エラー内容 | HTTP Status Code | JSON error |
---|---|---|
JSONリクエストがJSON-RPCのリクエストとして不正 | 200 OK | { "code": -32600, "message": "Invalid Request." } |
methodで指定したクラス/メソッドが見つからない(※2) | 200 OK | { "code": -32601, "message": "Method not found." } |
paramsが不適切(※3) | 200 OK | { "code": -32602, "message": "Invalid params." } |
JSONの解析に失敗した | 200 OK | { "code": -32700, "message": "Parse error." } |
その他のエラー | 200 OK | { "code": -32603, "message": "Internal error." or Throwable#getMessage() "data": Throwable } |
RESTモード
class変数の値が"rpc"以外の場合はRESTモードになります。RESTモードでは、GET/POST/PUT/DELETEの四つのHTTP Methodに従い対象となったクラスの次の名前のメソッドが呼び出されます(※4)。そして、実行後、戻り値がJSONに変換されクライアントに返されます(※5)。
HTTP Method | Java メソッド名 | 引数 |
---|---|---|
GET | find | リクエストパラメータのMapを「.」あるいは「[...]」で区切られた階層構造とみなし引数の型に従い変換し設定 |
POST | create | メッセージボディのJSON文字列を引数の型に従い変換し設定(※6) |
PUT | update | |
DELETE | delete |
なお、上記のメソッド名は変更することが可能です。その場合は対象のマッピングを配列化し、二番目の引数にHTTP Methodとメソッド名のマッピングを記載します。例えば、.csvに対してGETでアクセスされた場合にprintというメソッドを呼び出したい場合は次のようにします。
<servlet> ... <init-param> <param-name>config</param-name> <param-value> { "mappings": { // GET *.csvにアクセスされた場合はprintメソッドを呼び出します。 "/{package}/{class}.csv": ["sample.web.${package}.service.${class}Service", {"GET": "print"}] "/{package}/{class}.{ext}": "sample.web.${package}.service.${class}Service" } } </param-value> </init-param> </servlet>
引数には送信されたJSONが指定された型に従い変換されセットされます。引数への設定は、JSONの型によって次のような違いがあります。
Request Type | 説明 |
---|---|
object | パラメータ変数、パス変数の順に上書きされたJSON objectが設定されます(同じキーが複数出現した場合は配列化されます)。 |
array | 送信されたJSON arrayを引数リストとして扱います。 なお、第一引数がJSON objectである場合には、上記と同様にパラメータ変数、パス変数、第一引数の順でデータが上書きされる形で設定されます。 その他の型であった場合や第二引数以降はそのまま設定されます。 |
(※5) JSONはobjectかarrayより始まる必要があるため、それ以外の要素に変換される型の戻り値(例えば、boolean/int/Dateなど)の場合にはSC_NO_CONTENTが返されます。
(※6) Content-Typeが「
application/x-www-form-urlencoded
」の場合には、GETの場合と同様にフォームのパラメータ名を「.」で区切られた階層構造とみなして扱います。
RESTモードでは、エラーの発生はHTTP Status Codeによりクライアントに通知されます。
エラー内容 | HTTP Status Code |
---|---|
クラス/メソッドが見つからない(※7) | 404 Not found |
送信されたJSONの解析/変換に失敗した(※8) | 400 Bad request |
その他のエラー | 500 Internal Server Error |
JSONP
RESTモードでかつHTTP MethodがGETの場合、リクエストパラメータとしてcallback=Function名を指定することでJSONPによる返答を返すことができるようになります。
<script type="text/javascript" > function call(value) { alert(value); } </script> ... <script type="text/javascript" src="http://host/hoge.json?callback=call"></script>
初期化/終了処理
呼び出し対象となるクラスにinitあるいはdestroyという名前のメソッドがある場合、それぞれ処理の前後によびだされます。(※9)
public class HogeService { // 処理の前に呼び出されます。 public void init() { } ... // 処理の後に呼び出されます。 public void destroy() { } }
1.1からの変更点
- [不具合] パス変数に「.」を指定できるため、意図せぬクラスの呼び出しが可能になる場合があったため、デフォルトのパターンを
[^/().]+
に変更しました。 - 各種DI Container用Containerクラスのパッケージを
net.arnx.jsonic.web
からnet.arnx.jsonic.web.extension
に変更しました。ただし、WebServiceServletに限り互換性維持のため、以前のパッケージ名を指定しても動作します。 - Containerにリクエストの最初と最後に呼ばれるstartメソッドとendメソッドを追加しました(WebServiceServletでも利用できます)。