WebService
XML-RPC和JSON-RPC
Moqui有工具生产、消费XML-RPC、JSON-RPC服务。任意Service Facade可以通过设置service.allow-remote属性为true来作为一个远程可调用的服务暴露。
WebFacade有方法来接收这些RPC调用:ec.web.handleXmlRpcServiceCall()和ec.web.handleJsonRpcServiceCall()。在自带的webroot组件中有一个rpc.xml页面,有调用这些方法的xml和json转换。有了这设置,远程服务调用的路径是/rpc/sml和/rpc/json。
下面是一个JSON-RPC服务调用的例子,使用curl作为客户端。它调用org.moqui.example.ExampleServices.createExample服务,带name、type和status参数。它也传入用户名和密码来在运行服务前鉴定身份(遵循能被用于任何ServiceFacade服务调用的模式)。
id字段总是像1这样的值。这个JSON-RPC字段被用于多消息请求,请求中的每条消息将有一个不同的id值,那个值相应地被用在响应的id字段中。 多消息请求的JSON字符串会在外层有个列表包含像此例中的单个的消息。
curl -X POST -H "Content-Type:application/json"
--data{
"jsonrpc":"2.0",
"method":"org.moqui.example.ExampleServices.createExample",
"id":1,
"params":{
"authUsername":"john.doe",
"authPassword":"moqui",
"exampleName":"JSON-RPC Test 1",
"exampleTypeEnumId":"EXT_MADE_UP",
"statusId":"EXST_IN_DESIGN"
}
http://localhost:8080/rpc/json
运行这个的结果会像是这样(exampleId值可能不一样):
{
"jsonrpc":"2.0",
"id":1,
"result":{
"exampleId":"100050"
}
}
Moqui中的JSON-RPC实现遵循JSON-RPC 2.0规范。
XML-RPC请求遵循类似的模式。Moqui使用实现了XML-RPC规范 的Apache XML-RPC库 。
尽管你能编写代码直接使用一个库(或像RemoteJsonRpcServiceRunner.groovy中的自定义JSON处理)调用远程的XML-RPC和JSON-RPC服务,最容易的方法是用一个代理服务定义来调用远程服务。这么做:
- 定义一个服务
- service.type属性使用 remote-xml-rpc 或 remote-json-rpc
- 设置 service.location为RPC服务器和路径的URL(例如: http://localhost:8080/rpc/json ),或 匹配Moqui配置XML文件中的一个服务位置名(即service-facade.service-location.name)的值;为调用远程服务,自带有两个服务位置:main-xml和main-json;这些和额外期望的位置可在运行时Moqui配置XML文件中配置,然后用在你的服务位置中,来简化配置。特别是当测试和生产环境有不同的URL时。
- 设置 service.method为要调用的远程服务的名字;在JSON-RPC中这映射到method字段;在XML-RPC中这映射到methodName元素;当调用其他的Moqui服务器时,这是被调用的服务的名字
- 服务可以定义匹配远程服务定义的参数,或能被设置为不校验输入;你也能定义参数的默认值并为类型转换指定类型,类型转换在远程服务被调用前完成
当你本地调用此服务时,ServiceFacade将调用远程服务并返回结果。也就是说,你调用一个被配置为代理到远程服务的本地服务。
发送和接收简单的JSON
有时一个API规范调用一个特定的JSON结构或其他不是JSON-RPC信封的结构。WebFacade中有一些特性使得这会容易点。
当一个HTTP请求被接收(WebFacade被初始化时),如果请求的Content-Type(MIME类型)是application/json,将解析请求体中的JSON字符串,如果外层元素是一个Map(在JSON中是一个对象),那么Map中的条目将被添加到web参数(ec.web.parameters),并且web参数被自动添加到被渲染的页面或运行的页面转换的上下文(ec.context)。如果外层元素是一个List(JSON中的数组),那么它被放入一个_requestBodyJsonListweb参数,然后在上下文中可用。
这使得易于获取web请求中的JSON数据。它也解决了WebFacade自动在请求体中查找multi-part内容(WebFacade总是会做的)之后获取请求体的问题,因为Servlet容器可能不允许在这之后再次读取请求体。
对于一个JSON响应,你能通过设置HttpServletResponse上的多样东西手工将响应放在一起,并使用Groovy JsonBuilder来生成JSON文本。方便起见,ec.web.sendJsonResponse(Object responseObj)方法为你做了所有这些。
换个方向,请求一个接受JSON并以JSON响应的URL,有特殊的工具,因为Groovy和其他工具使得这非常容易。例如,这是一个远程调用一个JSON-RPC服务的实际代码的例子:
Map jsonRequestMap = [ jsonrpc:"2.0", id:1, method:method, params:parameters ]
JsonBuilder jb = new JsonBuilder()
jb.call(jsonRequestMap)
String jsonResponse = StupidWebUtilities.simpleHttpStringRequest(location, jb.toString(), "application/json")
Object jsonObj = new JsonSlurper().parseText(jsonResponse)
这使用Groovy中的JsonBuilder和JsonSlurper类和内部使用了Apache HTTP Client库的StupidWebUtilities.simpleHttpStringRequest()方法。
RESTful 接口
RESTful服务使用一个URL模式和请求方法来标识服务,而不是像JSON-RPC和XML-RPC那样的方法名。一般的想法是用URL来标识一条记录,记录类型作为路径元素,记录的ID作为一个或多个路径元素(简便起见通常是一个,即单字段主键)。
把这条记录当作一个web资源进行交互时,HTTP请求方法指定对这记录做什么。这非常像Moqui的entity-auto服务的create、update和delete服务动词。GET方法一般是查询记录,POST方法一般映射为创建记录。PUT方法一般映射到更新记录。DELETE方法是删除。
例如下面这个例子,见ExampleApp.xml文件。
要支持RESTful web service,需要一种方法让页面转换在一个基于web的应用中运行时对HTTP请求方法敏感。在Moqui框架中用transition.method属性来处理,像这样:
<transition name="ExampleEntity" method="put">
<path-parameter name="exampleId"/>
<service-call name="org.moqui.example.ExampleServices.updateExample"
in-map="ec.web.parameters" web-send-json-response="true"/>
<default-response type="none"/>
</transition>
要测试这个转换,使用一条像这样的curl命令来更新Example实体exampleId的值为100010的记录的exampleName字段:
curl -X PUT -H "Content-Type: application/json" \
-H "Authorization: Basic am9obi5kb2U6bW9xdWk=" \
--data '{ "exampleName":"REST Test - Rev 2" }' \
http://.../apps/example/ExampleEntity/100010
关于此例的一些要记住的、使得易于围绕内部Moqui服务创建REST包装的重要事情:
- 使用HTTP Basic 身份鉴定(john.doe/moqui),Moqui自动识别并用于身份鉴定
- 输入的JSON体自动映射到参数(JSON字符串必需有一个Map根对象)
- exampleId被作为路径的一部分传递,并用path-parameter元素将其当作一个普通的参数
- 使用 ec.web.parameters Map作为in-map来明确传递web参数到服务(也可以使用ec.context传递整个上下文,也包括web参数,但是用ec.web.parameters这种方法更明确和有限制)
- 用service-call.web-send-json-response属性和一个none类型的响应来发送JSON响应
在ExampleApp.xml文件中有多个处理RESTul服务请求的其他例子。