<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.visual-prolog.com/index.php?action=history&amp;feed=atom&amp;title=Web_Services_%28Web%E6%9C%8D%E5%8A%A1%29</id>
	<title>Web Services (Web服务) - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.visual-prolog.com/index.php?action=history&amp;feed=atom&amp;title=Web_Services_%28Web%E6%9C%8D%E5%8A%A1%29"/>
	<link rel="alternate" type="text/html" href="https://wiki.visual-prolog.com/index.php?title=Web_Services_(Web%E6%9C%8D%E5%8A%A1)&amp;action=history"/>
	<updated>2026-05-19T18:06:19Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.37.1</generator>
	<entry>
		<id>https://wiki.visual-prolog.com/index.php?title=Web_Services_(Web%E6%9C%8D%E5%8A%A1)&amp;diff=4162&amp;oldid=prev</id>
		<title>Yiding: Created page with &quot;（以下内容，译自:Category:Tutorials中的Web Services。更多内容可以参看:Category:Chinese。）  本教程描述如何创建Web应用程序，它的前端...&quot;</title>
		<link rel="alternate" type="text/html" href="https://wiki.visual-prolog.com/index.php?title=Web_Services_(Web%E6%9C%8D%E5%8A%A1)&amp;diff=4162&amp;oldid=prev"/>
		<updated>2015-08-23T09:23:43Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;（以下内容，译自&lt;a href=&quot;/index.php?title=Category:Tutorials&quot; title=&quot;Category:Tutorials&quot;&gt;Category:Tutorials&lt;/a&gt;中的Web Services。更多内容可以参看&lt;a href=&quot;/index.php?title=Category:Chinese&quot; title=&quot;Category:Chinese&quot;&gt;Category:Chinese&lt;/a&gt;。）  本教程描述如何创建Web应用程序，它的前端...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;（以下内容，译自[[:Category:Tutorials]]中的Web Services。更多内容可以参看[[:Category:Chinese]]。）&lt;br /&gt;
&lt;br /&gt;
本教程描述如何创建Web应用程序，它的前端是运行在用户浏览器中的HTML/JavaScript，而后端是用Visual Prolog编写的。&lt;br /&gt;
&lt;br /&gt;
介绍的重点是Visual Prolog的后端，它实现JSON-RPC Web服务。所包含的HTML/JavaScript代码主要是为了使例子自身完整，说明前端代码如何与Visual Prolog服务互动。&lt;br /&gt;
&lt;br /&gt;
教程描述了同样的服务如何才能：&lt;br /&gt;
&lt;br /&gt;
* 作为独立的HTTP/HTTPS服务器运行&lt;br /&gt;
* 作为IIS（Microsoft Information Services微软信息服务）的ISAPI扩展嵌入式地运行&lt;br /&gt;
&lt;br /&gt;
注意，web服务需要使用Visual Prolog的 &amp;#039;&amp;#039;&amp;#039;商业版&amp;#039;&amp;#039;&amp;#039;。&lt;br /&gt;
还要注意，本教程中完全没有考虑SOAP。&lt;br /&gt;
&lt;br /&gt;
使用的例子是商业版中的 &amp;#039;&amp;#039;&amp;#039;webRPC&amp;#039;&amp;#039;&amp;#039;。 (IDE: &amp;#039;&amp;#039;&amp;#039;Help -&amp;gt;Install Examples...&amp;#039;&amp;#039;&amp;#039;)。&lt;br /&gt;
&lt;br /&gt;
=== 概述 ===&lt;br /&gt;
&lt;br /&gt;
本节描述应用程序的整体概貌及工作方式。实际发生的事情是相当复杂的，不过好在很多复杂的东西是自动处理的。了解过程的概貌及其复杂性，就比较容易理解要做些什么及为什么这样做。&lt;br /&gt;
&lt;br /&gt;
总的来说，作为例子的应用程序其工作流程是这样的：用户（在web浏览器中）浏览网页 &amp;#039;&amp;#039;&amp;#039;http:&amp;lt;nowiki /&amp;gt;//&amp;lt;server&amp;gt;/file/test.htm&amp;#039;&amp;#039;&amp;#039; （或 &amp;#039;&amp;#039;&amp;#039;http:&amp;lt;nowiki /&amp;gt;//&amp;lt;server&amp;gt;/file/calendar.htm&amp;#039;&amp;#039;&amp;#039;），服务器返回相应的文件给浏览器。浏览器对文件的上下文进行评估后，可能进一步请求级联的css、js文件和图片等。此外，HTML文件包含的嵌入式JavaScript代码也会对服务器产生远端过程调用 (&amp;#039;&amp;#039;&amp;#039;RPC&amp;#039;&amp;#039;&amp;#039;)，服务器执行相应的过程并将结果返回给浏览器。最后，嵌入式的JavaScript用返回的数据更新浏览器的HTML内容。&lt;br /&gt;
&lt;br /&gt;
客户机与服务器之间的通信由HTTP (或 HTTPS)协议完成。因此在服务器一侧需要一个HTTP服务器，它可以返回文件并实现远端过程调用。在 &amp;#039;&amp;#039;&amp;#039;jsonRpcService_httpApi&amp;#039;&amp;#039;&amp;#039; 中的程序运行该服务作为一个独立的HTT服务器，它可以处理文件请求，实现远端过程调用。&amp;#039;&amp;#039;&amp;#039;jsonRpcService_isApi&amp;#039;&amp;#039;&amp;#039;中的程序是用ISAPI插件来实现远端过程调用的，通过对IIS的配置来处理文件请求并对远端过程调用使用ISAPI。&lt;br /&gt;
&lt;br /&gt;
独立的HTTP服务器通过微软的[http://msdn.microsoft.com/en-us/library/windows/desktop/aa364510(v=vs.85).aspx HTTP Server API]来实现，它可以共享带有IIS的端口及URL，这样一来，&amp;#039;&amp;#039;&amp;#039;http:&amp;lt;nowiki /&amp;gt;//&amp;lt;server&amp;gt;/file&amp;#039;&amp;#039;&amp;#039;等可以进入到我们的独立服务器而&amp;#039;&amp;#039;&amp;#039;http:&amp;lt;nowiki /&amp;gt;//&amp;lt;server&amp;gt;/&amp;lt;something&amp;gt;&amp;#039;&amp;#039;&amp;#039;也可以由IIS得到处理。&lt;br /&gt;
&lt;br /&gt;
=== JSON 和 JSON-RPC ===&lt;br /&gt;
&lt;br /&gt;
远端过程调用是按照[http://www.jsonrpc.org/specification JSON-RPC 2.0 Specification]规程实现的，这实际上要求数据按[http://www.json.org/ JSON] (&amp;#039;&amp;#039;&amp;#039;J&amp;#039;&amp;#039;&amp;#039;ava&amp;#039;&amp;#039;&amp;#039;S&amp;#039;&amp;#039;&amp;#039;cript &amp;#039;&amp;#039;&amp;#039;O&amp;#039;&amp;#039;&amp;#039;bject &amp;#039;&amp;#039;&amp;#039;N&amp;#039;&amp;#039;&amp;#039;otation) 格式编码。&lt;br /&gt;
&lt;br /&gt;
JSON语法是JavaScript的一个子集：在对有效JSON文本进行求值时它会成为一个相应的JavaScript对象。然而，还是应该总是使用JSON解析程序而不是对该文本求值：因为文本更可能是“代码”而不是“数据”，所以对文本求值有安全上的风险。&lt;br /&gt;
&lt;br /&gt;
不需要对JSON格式有详细的了解，因为在客户端一侧使用的是JavaScript而在服务器一侧使用的是JSON的对象/域表示方式，（对JSON格式的）解析和编写都可以交给标准软件去做。&lt;br /&gt;
&lt;br /&gt;
执行JSON-RPC调用时，客户端发送一个JSON请求对象，服务器用一个JSON响应对象做响应，除非请求是一个&amp;#039;&amp;#039;&amp;#039;notification（通知）&amp;#039;&amp;#039;&amp;#039;，此时不需要响应。&lt;br /&gt;
&lt;br /&gt;
请求对象有四个成员：&lt;br /&gt;
* &amp;lt;vp&amp;gt;jsonrpc&amp;lt;/vp&amp;gt;: 一个说明JSON-RPC协议版本的串，必须就是 &amp;quot;2.0&amp;quot;。&lt;br /&gt;
* &amp;lt;vp&amp;gt;method&amp;lt;/vp&amp;gt;: 一个包含被调用方法名称的串。&lt;br /&gt;
* &amp;lt;vp&amp;gt;params&amp;lt;/vp&amp;gt;: 该方法的参数（如果方法不带参数就省略）。&lt;br /&gt;
**by-position: 一个JSON数组，包含有按服务器要求顺序排列的值。&lt;br /&gt;
**by-name: 一个带有成员名称的JSON对象，成员名称要严格与方法所期望的参数相匹配，包括大小写都要一致。 &lt;br /&gt;
* &amp;lt;vp&amp;gt;id&amp;lt;/vp&amp;gt;: 一个由客户端创建的标识该请求的串或数。如果是通知则这个域就省掉了。&lt;br /&gt;
&lt;br /&gt;
响应对象有三个成员：&lt;br /&gt;
* &amp;lt;vp&amp;gt;jsonrpc&amp;lt;/vp&amp;gt; 一个说明JSON-RPC协议版本的串，必须就是 &amp;quot;2.0&amp;quot;。&lt;br /&gt;
* &amp;lt;vp&amp;gt;id&amp;lt;/vp&amp;gt; 由客户端发布的值。&lt;br /&gt;
第三个成员是下列之一：&lt;br /&gt;
* &amp;lt;vp&amp;gt;result&amp;lt;/vp&amp;gt; （意味着该调用成功了）程序调用的结果&lt;br /&gt;
* &amp;lt;vp&amp;gt;error&amp;lt;/vp&amp;gt; （意味着该调用失败了） 一个描述所发生错误的对象&lt;br /&gt;
&lt;br /&gt;
=== 客户端一侧 ===&lt;br /&gt;
&lt;br /&gt;
{{例|这段代码演示了在客户端一侧进行一次过程调用的方法：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;JavaScript&amp;quot;&amp;gt;&lt;br /&gt;
function httpPOST(URL, method, params) {&lt;br /&gt;
    var ReqObj = { jsonrpc: &amp;quot;2.0&amp;quot;, id: getId(), method: method, params: params };&lt;br /&gt;
    var Req = JSON.stringify(ReqObj);&lt;br /&gt;
    var xmlHttp = new XMLHttpRequest();&lt;br /&gt;
    xmlHttp.open(&amp;quot;POST&amp;quot;, URL, false);&lt;br /&gt;
    xmlHttp.setRequestHeader(&amp;#039;Content-Type&amp;#039;, &amp;#039;application/json-rpc; charset=UTF-8&amp;#039;);&lt;br /&gt;
    xmlHttp.send(Req);&lt;br /&gt;
    var resp = xmlHttp.responseText;&lt;br /&gt;
    var respObj = JSON.parse(resp);&lt;br /&gt;
    var error = respObj.error;&lt;br /&gt;
    if (error) {&lt;br /&gt;
        if (Data = error.data) {&lt;br /&gt;
            log(&amp;quot;&amp;lt;pre&amp;gt;&amp;quot; + Data +&amp;quot;&amp;lt;/pre&amp;gt;\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        log(&amp;quot;&amp;lt;p&amp;gt;error : &amp;quot;+ error.code +&amp;quot;: &amp;quot; + error.message + &amp;quot;&amp;lt;/p&amp;gt;\n&amp;quot;);&lt;br /&gt;
    } else {&lt;br /&gt;
        // respObj.result contains the result&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
小结一下：&lt;br /&gt;
* 创建一个请求对象Create a request object&lt;br /&gt;
* 由JSON将其 &amp;quot;串化（stringify）&amp;quot;&lt;br /&gt;
* 用一个XMLHttpRequest对象发布它&lt;br /&gt;
* JSON解析响应文本&lt;br /&gt;
* 对 &amp;quot;error&amp;quot; 成员进行检查&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
有很多其它的方法来写客户端一侧的代码，通常会使用一些标准的JavaScript库程序包，这些包可以异步处理错误及执行调用。 &amp;#039;&amp;#039;&amp;#039;calendar.htm&amp;#039;&amp;#039;&amp;#039; 使用了这样的库代码，不过这些内容超出了本教材的范围。&lt;br /&gt;
&lt;br /&gt;
=== 服务代码 ===&lt;br /&gt;
&lt;br /&gt;
在Visual Prolog中JSON是由域&amp;lt;vp&amp;gt;json::jsonValue&amp;lt;/vp&amp;gt;来表示的：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;domains&lt;br /&gt;
    jsonValue =&lt;br /&gt;
        n; % null&lt;br /&gt;
        f; % false&lt;br /&gt;
        t; % true&lt;br /&gt;
        r(real Number);&lt;br /&gt;
        s(string String);&lt;br /&gt;
        a(jsonValue* Array);&lt;br /&gt;
        o(jsonObject Object).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
JSON值只有7类：null、false、true、数、串、数组、对象。&lt;br /&gt;
&lt;br /&gt;
数组是一个JSON值的序列，用Visual Prolog的表来表示。&lt;br /&gt;
&lt;br /&gt;
JSON对象由Visual Prolog的 &amp;lt;vp&amp;gt;jsonObject&amp;lt;/vp&amp;gt; 接口表示：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;interface jsonObject supports mapM{string Key, jsonValue Value}&lt;br /&gt;
...&lt;br /&gt;
end interface jsonObject&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
该接口含有一些便利的谓词/属性（这些上面都没有说到），不过从本质上来说&amp;lt;vp&amp;gt;jsonObject&amp;lt;/vp&amp;gt;不过就是从Key（名称）到JSON值之间的映射。&lt;br /&gt;
&lt;br /&gt;
{{例|以下代码：&lt;br /&gt;
&amp;lt;vip&amp;gt;J = jsonObject::new(),&lt;br /&gt;
J:set_boolean(&amp;quot;bbb&amp;quot;, true),&lt;br /&gt;
J:set_string(&amp;quot;sss&amp;quot;, &amp;quot;The String&amp;quot;),&lt;br /&gt;
J:writeTo(stdio::outputStream),&lt;br /&gt;
stdio::nl,&lt;br /&gt;
jsonPrettyPrint::ppObject(stdio::outputStream, J)&amp;lt;/vip&amp;gt;&lt;br /&gt;
会生成如下输出：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;{&amp;quot;bbb&amp;quot;:true,&amp;quot;sss&amp;quot;:&amp;quot;The String&amp;quot;}&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;bbb&amp;quot; : true,&lt;br /&gt;
    &amp;quot;sss&amp;quot; : &amp;quot;The String&amp;quot;&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;vp&amp;gt;writeTo&amp;lt;/vp&amp;gt; 压缩生成一种适用于进程间通信等的 JSON 表示。 &amp;lt;vp&amp;gt;jsonPrettyPrint&amp;lt;/vp&amp;gt; 可用良好的格式（比如调试时用）打印该值。&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
创建 RPC 服务时，必须实现一个支持&amp;lt;vp&amp;gt;rpcService&amp;lt;/vp&amp;gt;接口的对象：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;interface testService supports rpcService&lt;br /&gt;
end interface testService&lt;br /&gt;
&lt;br /&gt;
class testService : testService&lt;br /&gt;
end class testService&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
在该实现中，需要继承处理JSON解析、编写等的&amp;lt;vp&amp;gt;jsonRpcServiceSupport&amp;lt;/vp&amp;gt;：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;implement testService&lt;br /&gt;
    inherits jsonRpcServiceSupport&lt;br /&gt;
    open core, json&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
end implement testService&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
还需要在构造器中注册你的过程：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;clauses&lt;br /&gt;
    new() :-&lt;br /&gt;
        setProc_name(&amp;quot;hello&amp;quot;, hello),&lt;br /&gt;
        setProc_name(&amp;quot;calendar&amp;quot;, calendar).&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
过程有四类，相应于不同的注册谓词：There are four kinds of procedures, with corresponding registration predicates:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;vp&amp;gt;setProc_array&amp;lt;/vp&amp;gt; 用于注册将参数放在数组中的过程&lt;br /&gt;
* &amp;lt;vp&amp;gt;setProc_name&amp;lt;/vp&amp;gt; 用于注册在对象中使用命名参数的过程&lt;br /&gt;
* &amp;lt;vp&amp;gt;setListener_array&amp;lt;/vp&amp;gt; 用于注册将参数放在数组中的通知监听器&lt;br /&gt;
* &amp;lt;vp&amp;gt;setListener_name &amp;lt;/vp&amp;gt; 用于注册在对象中使用命名参数的通知监听器&lt;br /&gt;
&lt;br /&gt;
在上面的例子中，有两个过程“hello”和“calendar”，都是使用命名参数。 hello 的代码可以是这样的：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vip&amp;gt;constants&lt;br /&gt;
    testService_error : jsonRpcError::serverErrorCode = jsonRpcError::serverError_last+1.&lt;br /&gt;
    % 从last开始增加，last是用于未指明的服务器错误&lt;br /&gt;
&lt;br /&gt;
predicates&lt;br /&gt;
    hello : jsonProc_name.&lt;br /&gt;
clauses&lt;br /&gt;
    hello(_Context, ArgMap) = o(Result) :-&lt;br /&gt;
        Result = jsonObject::new(),&lt;br /&gt;
        Username = ArgMap:get_string(&amp;quot;username&amp;quot;),&lt;br /&gt;
        if &amp;quot;error&amp;quot; = Username then&lt;br /&gt;
            raise_serviceError(testService_error, &amp;quot;&amp;#039;error&amp;#039; is an invalid user name&amp;quot;)&lt;br /&gt;
        elseif &amp;quot;userError&amp;quot; = Username then&lt;br /&gt;
            exception::raise_user(&amp;quot;&amp;#039;%Name%&amp;#039; is an invalid user name&amp;quot;, [namedValue(&amp;quot;Name&amp;quot;, string(Username))])&lt;br /&gt;
        elseif &amp;quot;definiteUserError&amp;quot; = Username then&lt;br /&gt;
            exception::raise_definiteUser(&amp;quot;&amp;#039;%Name%&amp;#039; is an invalid user name&amp;quot;, [namedValue(&amp;quot;Name&amp;quot;, string(Username))])&lt;br /&gt;
        else&lt;br /&gt;
            Result:set_string(&amp;quot;token&amp;quot;, string::concat(&amp;quot;Hello &amp;quot;, Username))&lt;br /&gt;
        end if.&amp;lt;/vip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;hello&amp;lt;/vp&amp;gt;过程首先创建一个&amp;lt;vp&amp;gt;jsonObject&amp;lt;/vp&amp;gt;用于&amp;lt;vp&amp;gt;Result&amp;lt;/vp&amp;gt;。任意 JSON 值都可以当成结果，不过 &amp;lt;vp&amp;gt;hello&amp;lt;/vp&amp;gt; 设计为返回带一个&amp;lt;vp&amp;gt;&amp;quot;token&amp;quot;&amp;lt;/vp&amp;gt;成员的JSON对象。&lt;br /&gt;
&lt;br /&gt;
接下来它由&amp;lt;vp&amp;gt;ArgMap&amp;lt;/vp&amp;gt;获取参数，要的是一个名为&amp;lt;vp&amp;gt;&amp;quot;username&amp;quot;&amp;lt;/vp&amp;gt;的串参数。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;hello&amp;lt;/vp&amp;gt; 使用 &amp;lt;vp&amp;gt;Username&amp;lt;/vp&amp;gt; 的值来说明异常处理与成功。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;&amp;quot;error&amp;quot;&amp;lt;/vp&amp;gt;一段演示如何引发“服务器错误”，它是用于RPC服务的特别设计的错误消息。“userError”和“definiteUserError”情况说明的是该过程调用时除其它异常外可能发生的异常。&lt;br /&gt;
&lt;br /&gt;
要注意，客户端代码要编程处理相应的错误。当然服务器一侧也可以在任意其它程序中处理指定的异常，也可以在适当地方记录这些错误。&lt;br /&gt;
&lt;br /&gt;
错误不是 &amp;lt;vp&amp;gt;Username&amp;lt;/vp&amp;gt; 的情况，映射 &amp;quot;token&amp;quot; 串给 &amp;lt;vp&amp;gt;Result&amp;lt;/vp&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
{{例|建议 &amp;lt;vp&amp;gt;jsonProc&amp;lt;/vp&amp;gt; 只做参数提取与结果构造，而使实际功能在分开的事务层中，如下面这段代码演示的：&lt;br /&gt;
&amp;lt;vip&amp;gt;predicates&lt;br /&gt;
    hello : jsonProc_name.&lt;br /&gt;
clauses&lt;br /&gt;
    hello(_Context, ArgMap) = o(Result) :-&lt;br /&gt;
        Result = jsonObject::new(),&lt;br /&gt;
        Username = ArgMap:get_string(&amp;quot;username&amp;quot;),&lt;br /&gt;
        Token = businessLayer::hello(Username),  % 实际功能在分开的事务层中&lt;br /&gt;
        Result:set_string(&amp;quot;token&amp;quot;, Token).&amp;lt;/vip&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
=== 独立的 HTTP 服务器 ===&lt;br /&gt;
&lt;br /&gt;
如同上面 &amp;lt;vp&amp;gt;jsonRpcService_httpApi&amp;lt;/vp&amp;gt; 程序演示的，&amp;lt;vp&amp;gt;testService&amp;lt;/vp&amp;gt;（支持 &amp;lt;vp&amp;gt;rpcService&amp;lt;/vp&amp;gt;）可以用于独立的 HTTP 服务器，不需要多少代码，而且为适合特殊需要而做的代码调整也很简单。但不管怎么样，也还是相当复杂的。&lt;br /&gt;
&lt;br /&gt;
该服务器基于微软能与IIS很好配合的[http://msdn.microsoft.com/en-us/library/windows/desktop/aa364510(v=vs.85).aspx HTTP Server API]。其实，IIS好像是构建在该 HTTP Server API 的顶层（或起码它们两者都是构建在共同基础之上的）。&lt;br /&gt;
&lt;br /&gt;
HTTP Server api 与以下实体打交道：&lt;br /&gt;
* 创建器/构造器的进程&lt;br /&gt;
* Server会话&lt;br /&gt;
* URL组&lt;br /&gt;
* Request队列&lt;br /&gt;
* Worker进程&lt;br /&gt;
&lt;br /&gt;
创建器/构造器进程创建服务器会话，由其定义URL组及请求队列。worker进程与请求队列相关联，处理队列接收到的请求。&lt;br /&gt;
&lt;br /&gt;
在示例程序中创建器/构造器是仅有的worker进程，更复杂的情形超出了本教材的范围。&lt;br /&gt;
&lt;br /&gt;
服务器会话是URL组及请求队列的所有者/持有者，它们才是我们需要实际考虑的。 &lt;br /&gt;
&lt;br /&gt;
URL组是由URL模型集定义的，其形式为：&lt;br /&gt;
:&amp;#039;&amp;#039;&amp;#039;http:&amp;lt;nowiki /&amp;gt;//&amp;lt;host&amp;gt;:&amp;lt;port&amp;gt;/&amp;lt;relativeURI&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
:&amp;#039;&amp;#039;&amp;#039;https:&amp;lt;nowiki /&amp;gt;//&amp;lt;host&amp;gt;:&amp;lt;port&amp;gt;/&amp;lt;relativeURI&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
一个请求到达计算机时，一种匹配机制会按照活动的URL组中URL模型集对其进行匹配，结果要不是匹配不上就是覆盖该URL。&lt;br /&gt;
&lt;br /&gt;
每个URL组分配一个请求队列，与某个URL组匹配的请求就放入其相应的队列。那些没有分配请求队列的URL组是非活动的，不参加匹配。&lt;br /&gt;
&lt;br /&gt;
Worker进程从请求队列中取出请求并进行处理。一个请求队列可以与若干个URL组相联系，而每个URL组又可以有若干URL模型。因此，worker进程由某个请求队列摘出的请求可以会匹配与该队列联系的某个URL组的某个URL模型。&lt;br /&gt;
&lt;br /&gt;
在示例服务器中，我们创建了两个请求队列，每个都只有一个URL组与之联系，并且每个URL组也只有一个URL模型。事实上：&lt;br /&gt;
* &amp;lt;vp&amp;gt;reqQueue_rpc&amp;lt;/vp&amp;gt; 接收来自 &amp;#039;&amp;#039;&amp;#039;http:&amp;lt;nowiki/&amp;gt;//localhost:5555/jsonrpc/&amp;lt;something&amp;gt;&amp;#039;&amp;#039;&amp;#039;的请求，而&lt;br /&gt;
* &amp;lt;vp&amp;gt;reqQueue_file&amp;lt;/vp&amp;gt; 接收来自&amp;#039;&amp;#039;&amp;#039;http:&amp;lt;nowiki/&amp;gt;//localhost:5555/file/&amp;lt;something&amp;gt;&amp;#039;&amp;#039;&amp;#039;&amp;#039;的请求。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;reqQueue_rpc&amp;lt;/vp&amp;gt; is a &amp;lt;vp&amp;gt;requestQueue_rpc&amp;lt;/vp&amp;gt; handles RPC requests by feeding them to our &amp;lt;vp&amp;gt;testService&amp;lt;/vp&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;reqQueue_file&amp;lt;/vp&amp;gt;是一个&amp;lt;vp&amp;gt;requestQueue_file&amp;lt;/vp&amp;gt;，它用 &amp;lt;vp&amp;gt;fileMapper&amp;lt;/vp&amp;gt;映射该请求到一个文件名及相应的&amp;lt;vp&amp;gt;ContentType&amp;lt;/vp&amp;gt;，&amp;lt;vp&amp;gt;requestQueue_file&amp;lt;/vp&amp;gt;接下来就会处理该文件的传输。&lt;br /&gt;
&lt;br /&gt;
==== 服务器配置：netsh ====&lt;br /&gt;
&lt;br /&gt;
上面的示例运行很平稳，因为它使用的是宿主机5555端口。然而一般情况下运行应用程序会要求http的使用80端口、https的使用443端口，这时就会出现一些安全性问题。&lt;br /&gt;
&lt;br /&gt;
如果仅只是更换端口号并使用公共宿主机名，在给URL组添加URL模型时服务器程序将会得到一个 &amp;#039;Access is denied&amp;#039;（拒绝访问）的异常。&lt;br /&gt;
&lt;br /&gt;
如果&amp;quot;as Administrator&amp;quot;来运行服务器则不会产生访问违例。但这绝对是不值得提倡的，因为这样一来它也有了做某些有害事情的权力。&lt;br /&gt;
&lt;br /&gt;
要使程序运行于普通模式，必须要对运行服务器程序的用户保留上面讨论的URL模型。&lt;br /&gt;
&lt;br /&gt;
使用netsh.exe (net shell)程序可以完成这个工作。 program.这将会需要处理http URL acl。详细内容参见 [http://msdn.microsoft.com/en-us/library/windows/desktop/cc307236(v=vs.85).aspx Netsh commands for HTTP].&lt;br /&gt;
&lt;br /&gt;
{{例|演示在命令行提示符下的工作：&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&amp;gt;netsh http add urlacl http://www.something.com:80/file/ user=SMT\serviceuser&lt;br /&gt;
&amp;gt;netsh http add urlacl http://www.something.com:80/jsonrpc/ user=SMT\serviceuser&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;注意&amp;#039;&amp;#039;&amp;#039; 要做修改需使命令行提示符运行于&amp;quot;as Administrator&amp;quot;。&lt;br /&gt;
如果仅写 &amp;quot;netsh&amp;quot; 就会进入http模式的提示符模式，只是写http命令。&lt;br /&gt;
&amp;lt;source lang=&amp;quot;text&amp;quot;&amp;gt;&amp;gt;netsh&lt;br /&gt;
netsh&amp;gt;http&lt;br /&gt;
netsh http&amp;gt;add urlacl http://www.something.com:80/file/ user=SMT\serviceuser&lt;br /&gt;
netsh http&amp;gt;add urlacl http://www.something.com:80/jsonrpc/ user=SMT\serviceuser&lt;br /&gt;
netsh http&amp;gt;exit&amp;lt;/source&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
通过Visual Prolog来进行URL保留也是可以的，但为了防止冲突及其它一些问题，最好还是使用标准netsh程序。&lt;br /&gt;
&lt;br /&gt;
注意示例是个控制台程序。在大规模应用情况下设计一个通用窗口服务会更好些。Visual Prolog有工程模板用于创建服务，但这超出了本教材的范围。&lt;br /&gt;
&lt;br /&gt;
=== IISAPI插件 ===&lt;br /&gt;
&lt;br /&gt;
如同 &amp;lt;vp&amp;gt;jsonRpcService_isApi&amp;lt;/vp&amp;gt; 程序所演示的那样，&amp;lt;vp&amp;gt;testService&amp;lt;/vp&amp;gt;（支持&amp;lt;vp&amp;gt;rpcService&amp;lt;/vp&amp;gt;)也可以ISAPI dll的形式来应用。该DLL可以由IIS使用来运行RPC请求。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;vp&amp;gt;jsonRpcService_isApi&amp;lt;/vp&amp;gt; 不处理文件请求，更自然的办法是把它留给IIS本身。该DLL代码其实不复杂，关键是IIS的配置。&lt;br /&gt;
&lt;br /&gt;
IIS配置有专门教材，参见[[IIS configuration]]。&lt;br /&gt;
&lt;br /&gt;
=== GUI程序 ===&lt;br /&gt;
&lt;br /&gt;
在&amp;#039;&amp;#039;&amp;#039;webRPC&amp;#039;&amp;#039;&amp;#039;示例目录中还有一个&amp;lt;vp&amp;gt;jsonRpcService_httpGUI&amp;lt;/vp&amp;gt;程序，它演示了图形用户界面程序如何用作独立Web RPC服务。要注意，并不提倡实际的服务程序具有GUI。这只是说明，当需要其它程序与GUI程序交互时GUI很有用，比如当用户登录时。它也可以用作一种快速借用已有GUI代码用于Web服务的方法（比如在创建实际服务前的试验）。&lt;br /&gt;
&lt;br /&gt;
=== 多线程 ===&lt;br /&gt;
&lt;br /&gt;
不管web服务如何运行（独立的带或不带GUI、或是ISAPI），需要注意的是HTTP请求运行于并发的若干线程。所以，保证请求处理的线程安全是很重要的。&amp;lt;vp&amp;gt;requestQueue_rpc::synchronizer&amp;lt;/vp&amp;gt;属性可用于同步RPC的执行，这个属性可以由一个谓词进行设置接收&amp;lt;vp&amp;gt;runnable&amp;lt;/vp&amp;gt;，而&amp;lt;vp&amp;gt;runnable&amp;lt;/vp&amp;gt;时才实际执行RPC调用。可运行的程序可以是，比如在一个监控程序（monitor）下运行，以此来保证这个时间只有一个程序在运行；或者是放入一个队列并从中顺序执行；再或者是如 &amp;lt;vp&amp;gt;jsonRpcService_httpGUI&amp;lt;/vp&amp;gt; 那样发布给GUI队列交由GUI线程执行。&lt;br /&gt;
&lt;br /&gt;
=== Visual Prolog 客户端 ===&lt;br /&gt;
&lt;br /&gt;
本教材及所提及的示例主要讨论Visual Prolog服务器一侧内容，而使用web浏览器作为客户端。不过，有时Visual Prolog程序也需要作为客户端访问WEB服务。示例 &amp;lt;vp&amp;gt;jsonRpcClient&amp;lt;/vp&amp;gt; 演示了如何做这样的事情。其方法与使用JavaScript时很相似：&lt;br /&gt;
&lt;br /&gt;
* 创建一个带有方法名及参数的 &amp;lt;vp&amp;gt;jsonRpcRequest&amp;lt;/vp&amp;gt; 对象&lt;br /&gt;
* 用 &amp;lt;vp&amp;gt;asString&amp;lt;/vp&amp;gt; 对其进行JSON串化&lt;br /&gt;
* 用 XMLHttpRequest 对象（&amp;lt;vp&amp;gt;xmlHTTP&amp;lt;/vp&amp;gt; 或 &amp;lt;vp&amp;gt;xmlHTTP60&amp;lt;/vp&amp;gt;的一个实例）进行投递&lt;br /&gt;
* JSON 解析响应文本（用&amp;lt;vp&amp;gt;jsonObject::fromString&amp;lt;/vp&amp;gt;）&lt;br /&gt;
* 检查 &amp;quot;error&amp;quot; 成员&lt;br /&gt;
&lt;br /&gt;
在示例程序中，有一个 &amp;lt;vp&amp;gt;delayCall&amp;lt;/vp&amp;gt;，,它是为了使GUI得到更新。如果没有它，GUI的更新就要一直等到RPC完成。不过这个调用与RPC本身是无关的。&lt;br /&gt;
&lt;br /&gt;
=== Skype HTTP/HTTPS 冲突 ===&lt;br /&gt;
{{:Include/SkypeHTTPConflict}}&lt;br /&gt;
&lt;br /&gt;
=== HTTPS ===&lt;br /&gt;
&lt;br /&gt;
HTTPS是使用安全套接的HTTP协议。IIS和独立HTTP服务器都可以使用HTTPS。要使用HTTPS，服务器计算机必须要有一个证书。可以自己创制证书，但自制的证书是不可信的，因而会引发安全提示。自制证书用于试验与开发是够用了，但在实际使用时需要获取一个可信的证书。&lt;br /&gt;
&lt;br /&gt;
简单说，可信证书必须从可信提供者那里获取。这种信任基于以下三件事：&lt;br /&gt;
* 提供者是自身可信的&lt;br /&gt;
* 提供者（通过正常购买链）对证书持有者真实身份具有认知&lt;br /&gt;
* 提供者可以在证书被滥用时撤消证书&lt;br /&gt;
&lt;br /&gt;
注意，HTTPS缺省时使用443端口（而HTTP使用80端口），需要时必须在使用&amp;quot;http&amp;quot;的地方写明 &amp;quot;https&amp;quot;（例如&amp;#039;&amp;#039;&amp;#039;https:&amp;lt;nowiki /&amp;gt;//+:443/mypath&amp;#039;&amp;#039;&amp;#039;）&lt;br /&gt;
&lt;br /&gt;
对证书的进一步讨论超出了本教材的范围。&lt;br /&gt;
&lt;br /&gt;
=== 参考 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.json.org/ JSON]&lt;br /&gt;
* [http://www.jsonrpc.org/specification JSON-RPC 2.0 Specification]&lt;br /&gt;
* Microsoft&amp;#039;s [http://msdn.microsoft.com/en-us/library/windows/desktop/aa364510(v=vs.85).aspx HTTP Server API]&lt;br /&gt;
&lt;br /&gt;
[[Category:Tutorials部分中文译文]]&lt;/div&gt;</summary>
		<author><name>Yiding</name></author>
	</entry>
</feed>