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页面,有调用这些方法的xmljson转换。有了这设置,远程服务调用的路径是/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中的JsonBuilderJsonSlurper类和内部使用了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服务请求的其他例子。

results matching ""

    No results matching ""