AWDwR "Chapter20 Web Services on Rails"

Chapter20を書いた人&Action Web Serviceのコード書いた人は南アフリカの人だって。Chapter18書いた人はオーストリアだし、DHHはデンマークだし、グローバルだね。
Action Web Serviceは長いんでAWSと省略するよ。

What AWS Is (and What It Isn't)

AWSではSOAPWSDLW3Cの仕様を全部実装したりXML-RPCの機能を全て提供するつもりはなくて、その代わりWebサービスで使いたい機能にフォーカスしている。このAWSを使うと

  • RailsのブログアプリケーションにBlogger APIやmetaWeblog APIのサポートを追加できる
  • 独自のAPIを実装して.NET開発者にWSDLからクラスを生成してもらえるようになる
  • 同じコードでSOAPXML-RPCの両方をサポートできる

The API Definition

AWSを使う時に問題となるのは、Rubyのメソッドは任意の型を返すことができるけれど、受け取り側がRubyのようなダイナミックな言語じゃない場合は想定していない型が返ってくるとエラーになっちゃうこと。そこで型をちゃんと定義しておくためのAPI定義クラスというのを用意しておきました。これも以下のようにジェネレータを使うと自動生成できる。

ruby script/generate web_service Backend find_all_products find_product_by_id

こうするとスタブAPI定義のBackendApiというクラスとスケルトンコントローラのBackendControllerというクラスとそれから機能テスト用のBackendApiTestというクラスができる。

コントローラでは、wsdl_service_name()メソッドでWSDLで使われる名前を設定し、web_service_scaffold()で開発時にWebブラウザからWebサービスを呼べる機能を追加する。

API定義はapi_method()メソッドを使って行なう。引数や戻り値の型の指定はシグナチャで指定。api_method()メソッドは引数の型を指定する:expectsと戻り値の型を指定する:returnsの2つのオプション受け取り、:expectsを省略した場合はリモートから引数付で呼び出されるとエラーとなり、:returnsを省略した場合はリモートへは何も返さない。

で、定義の仕方なんだけど、まず基本型は以下のようなシンボルで指定する。

これら以外にもActionWebService::StructやActiveRecord::Baseのオブジェクトをシグナチャとして使うことができる。

で、シグナチャの書き方の例

  • [ [:string] ] 文字列の配列
  • [:bool] ブーリアン
  • [Person] Person型
  • [{:lastname=>:string}] lastnameという名前つきの文字列型
  • [:int, :int] 2つの整数型

Dispatching Modes

リモートからのリクエストとメソッドを結びつけるのがディスパッチなんだけど、デフォルトのディスパッチモードはDirect Dispatching。これを使う場合は特に設定は不要。Direct Dispatchingの場合、API定義は直接コントローラに結び付けられ、APIメソッドはコントローラのパブリックなインスタンスメソッドとして実装する。この方法はシンプルなんだけど、一つのAPI定義しかコントローラに結び付けられないのが欠点。Layered Dispatchingは一つのコントローラに全てのAPIを結び付け、Delegated DispatchingはいくつかのAPIを一つのコントローラに結びつける。このディスパッチングモードはコントローラ内でweb_service_dispatching_mode()で指定できる。

Using Alternate Dispatching

Layered Dispatchingを使う場合はリクエストと呼び出すメソッドのマッピングをweb_serevice()メソッドで定義しておく。Delegated Dispatchingもweb_service_dispatching_mode()に:delegateを指定する以外は同じ。

Method Invocation Interception

AWSにはリクエストの前または後で起動されるコールバックを登録することができるbefore_invocation()とafter_invocation()というのがあって、ActionPackのフィルタと同じ感じで以下のように使う。

class BackendAuthController < ApplicationController
  before_invocation :authenticate

  protected
  def authenticate(name, args)
    ...
  end
end

before_invocation()メソッドで呼ばれるメソッドにはインターセプトしたメソッド名とその引数の配列が渡され、after_invocation()で呼ばれるメソッドはそれ以外にインターセプトしたメソッドの戻り値が渡される。またbefore_invocation()メソッド、after_invocation()メソッドには適用するメソッド、適用しないメソッドを指定する:onlyと:exceptというオプションがある。

Testing Web Services

Railsのテスト機能をAWSでも使えるよ。ジェネレータで機能テスト用のスケルトンが生成されるのでそれを書き換えて使いましょう。invoke(method_name, *args)メソッドはWebサービスを呼び出すメソッド。ただしこのinvoke()メソッドはDirect Dispatchingのコントローラに対してのみで、Layered Dispatchingの場合はinvoke_layered()メソッド、Delegated Dispatchingの場合はinvoke_delegated()を使いましょう。

で、WSDLを取得するには以下のURLにアクセスする。

http://www.foo.com/CONTROLLER/service.wsdl

また、XML-RPCの場合はエンドポイントURLを知る必要があって、Direct DispatchingとLayered Dispatchingの場合のエンドポイントは

http://www.foo.com/PATH/TO/CONTROLLER/api

Delegated Dispatchingの場合のエンドポイントURLは

http://www.foo.com/PATH/TO/CONTROLLER/SERVICE_NAME

となる。

Protocol Clients

AWSにはリモートのWebサービスにアクセスするためのクライアントの機能もあって、web_client_api()というメソッドを使って

class MyController < ApplicationController
  web_client_api :product,
                 :soap,
                 "http://www.foo.com/backend/api"
  ...
end

とできる。