创建
先用generator 来创建service 参数为
generator web_service ServiceOne method_a method_b
rails会创建一个service_one_api.rb在/app/apis下,同时有service_one_controller在/app/controllers/下
注意 因为ruby完全动态,所以无法根据方法接口来自动生成webservice, 所以搞了一个xxx_api.rb来干这个事情, 注意 rails对于对象之间的关系不会暴露到webservice外面去,只是简单的把字段id写出去
方法申明
在xxxx_api.rb中负责定义方法的参数, 定义一个web方法如下
api_method :xxxmethod_name, :expects=>... ,:returns =>
#:expects 如果忽略表示不能传递参数 :returns 忽略表示返回为空
它们接收的参数只能是如下情况之一
* symbol或者string 的基本类型
* Class类型只支持ActionWebService::Struct或者ActiveRecord::Base子类
* 一个包含前面2个类型参数的数组
* 一个包含前面3个类型参数的hash 用来表明webservice参数名 (webservice友好)
比如 [[:string]] [Person] [{:lastname=>:string}] [:int,:int]
基本类型为 :int :string :base64(会自动转为binary来传送文件) :bool :float :time :datetime(Ruby的DateTime) :date(Ruby的Date) 除此以为均非法
在Contoller中有如下指令
* wsdl_service_name 'SomeName' 设定当前service的名字(说不是必须的)
* wsdl_namespace 'http://xxx' 设定namespace 默扔为'urn:ActionWebService'
* web_service_api XXXApi 关联controller和他的api类,如果是符合命名规范,可以省略此指令
* web_service_scaffold :somemethod 生成一个action能提供一个直接体验webservice的web界面,方便调试
* web_service_dispatching_mode :layered | :delegated 设置dispatch mode, 省略表示 direct
ActionWebService::Struct 使用
这个类是用来帮助组织webservice可以识别的数据对象(DTO) 通过member方法来定义域和类型, 例子如下
class Person < ActionWebService::Struct
member :id, :int
member :name, :string
end
webservice 分派方式
通过web_service_dispatching_mode来申明分派方式, 注意不要写错了,写错了rails不报错,而且能给出无用但是合付xml文法的wsdl(一个没有任何方法的服务)
* 直接分配, 实现写在生成的controller中
layered dispatching 单独实现ActionWebService::Base的子类(放在apis目录下),定义public方法即可
class ProductService < ActionWebService::Base
web_service_api ProductApi
def find_all_products Product.find(:all).map{ |product| product.id end }
def find_product_by_id(id) Product.find(id) end end
delegated dispatching
申明web_service_dispatching_mode以后,使用web_service :my_serv_name ,XXXXService.new(相对静态生成) 或者web_service :my_name {XXXService.new} 作延迟加载(可以在block中访问controller的变量了,同时也可以对my_name这个新添加的service_action作filter 了)
具体的三种方法我还没有感觉出有什么特别用处,目前对webservice认识还不足
对webservice作 AOP 拦截
支持 before_invocation和after_invocation (:only 和 :except 语法), 如果before_invocation返回false或者抛出异常或者直接return[false,"reason"], 调用都会中止
拦截方法接收两个参数, 一个method_name, 一个method参数数组) 和 其他拦截类似, 还可以传入block(|sourceobj,m_name,m_params|) 和实现拦截类(只要实现interceptor(m_name,m_params)来拦截
webservice的测试
默认已经生成好functional test
使用invoke来调用直接的servcie, 类似还有
invoke_layered(service_name, method_name, *args) invoke_delegated(service_name, method_name, *args)
url对应关系
SOAP
默认controller有一个wsdl的action可以得到wsdl描述文件,通过service.wsdl也一样可以得到(routes.rb创建的),通过这个wsdl就可以得到所有的url信息了
XML-PRC (没有wsdl的情况) (其实在wsdl下方可以看到这些url,一样是对XML-PRC有效的
layered dispatching
http://host/PATH/TO/CONTROLLER/api
delegated dispatching
http://host/PATH/TO/CONTROLLER/SERVICE_NAME
这里的SERVICE_NAME就是web_service()方法的第一个参数
调用外部webservice
在rails controller内部,通过 web_client_api :product,:soap, 'http://url' 就可以创建一个product方法代理服务,使用product.xxx即可
还可以使用ActionWebService::Client::Soap或者ActionWebService::Client::XmlRpc 来基于Api定义的类直接创建对象 shop
=ActionWebService::Client::Soap.new(ProductApi,"http://my.app.com/backend/api") product = shop.find_product_by_id(5)
如果服务和rails关联不紧密, 就使用ruby的webservice包,不必用rails的了