这篇文章主要介绍了使用Python下的XSLT API进行web开发的简单教程,本文来自于IBM官方网站技术文档,需要的朋友可以参考下
[b]Kafka 样式的 soap 端点
Christopher Dix 所开发的“Kafka ― XSL SOAP 工具箱”(请参阅 参考资料)是一种用于构造 SOAP 端点的 XSLT 框架。它只涵盖了 SOAP 1.1,但 Kafka 端点演示了传递 UserLand SOAP 验证器(UserLand SOAP Validator)的能力,并且根据 SOAP 1.2 对它进行更新似乎并不太困难。 清单 1展示了一个样本 Kafka 端点:求两数之和的 SOAP 服务器(一个典型而简单的 SOAP 样本)。
清单 1. 求两数之和的 Kafka SOAP 端点
Addhttp://www.topxml.com/
XSLT 端点导入 SOAP 框架(文件 kafka/soap.xsl),然后设置该框架将要使用的参数,并设置它在处理构成 SOAP 消息的整个 XML 文档的过程中将要分派的模板。全局变量 Method 和 MethodNS 声明了组成消息的 XML 元素。在处理完 SOAP 信封之后,该框架调用 Processpayload 模板,该模板传入了 XML 主体的有效负载。 xsl:for-each 是将上下文切换成想要的节点的标准技巧。参数 A 和 B 是使用简单 XPaths 从这个元素读取的,而框架被再次调用以帮助写出响应参数。 WriteParameter 模板让您指定元素名称、数据类型和每个输出参数的值。本示例中的响应值是将两个输入参数相加所得的结果。
将这个端点部署为服务器相当于设置一个 HTTP 侦听器。Python 的 BaseHTTPServer 模块向您提供了所需的机制,能够轻而易举地处理该协议的 HTTP 部分。请参阅 清单 2。
清单 2. 用于清单 1 中所实现的 Kafka SOAP 端点的 Python HTTP 框架
#HTTP Listener code for SOAP server import BaseHTTPServer #The processor class is the core of the XSLT API from Ft.Xml.Xslt import Processor #4XSLT uses an InputSource system for reading XML from Ft.Xml import InputSource SOAP_IMPL_FILE = "add.xsl" class KafkaSoapHandler(BaseHTTPServer.BaseHTTPRequestHandler): def init(cls): from Ft.Lib import Uri #Set up a processor instance to use KafkaSoapHandler.processor = Processor.Processor() #Load it with add.xsl add_uri = Uri.OsPathToUri(SOAP_IMPL_FILE, attemptAbsolute=1) transform = InputSource.DefaultFactory.fromUri(add_uri) KafkaSoapHandler.processor.appendStylesheet(transform) #Now the processor is prepped with a transform and can be used #over and over for the same transform return #Make init() a static method of the class init = classmethod(init) def do_POST(self): clen = self.headers.getheader('content-length') if clen: clen = int(clen) else: print 'POST ERROR: missing content-length' return if self.path != '/add': self.send_error(404) input_body = self.rfile.read(clen) #input_body is the request SOAP envelope and contents response_body = self._run_through_kafka(input_body) #response_body is the response SOAP envelope and contents self._send_response(200, 'OK', response_body) return def _run_through_kafka(self, body): #In 4Suite all InputSources have base URIs in case they refer to #other URIs in some way and resolution is required. #The SOAP messages will not have any such URI references, #So use a dummy base URI source = InputSource.DefaultFactory.fromString(body, "urn:dummy") response = self.processor.run(source) return response def _send_response(self, code, msg, body): #Prepare a normal response self.send_response(200, 'OK') #Send standard HTP headers self.send_header('Content-type','text/html; charset=utf-8') self.send_header("Connection", "close") self.send_header("Accept-Ranges", "bytes") self.send_header('Content-length', len(body)-1) self.end_headers() #Send the response prepared by the SOAP end point self.wfile.write(body) return listen_on_port = 8888 #Set up to run on local machine server_address = ('127.0.0.1', listen_on_port) KafkaSoapHandler.init() httpd = BaseHTTPServer.HTTPServer(server_address, KafkaSoapHandler) print "Listening on port", listen_on_port #Go into a the main event loop httpd.serve_forever()
我们详细地注释了该清单,因此它应该是易于理解的。请注意,这段代码非常简单,这是因为它仅需处理该协议的 HTTP 部分,而将 XML 和 SOAP 部分的工作交由 Kafka 框架完成。该服务器专用于一个端点,因此它只须对 XSLT 转换进行一次解析和设置,然后它就可以简单地反复为每次新的请求运行该转换。这就是将处理器设置迁移到特殊的类方法中的原因,处理程序一注册到服务器就立即调用该方法。 classmethod 内置方法是 Python 2.2 中的新功能,实际上该版本是本例和后面的示例所必需的版本。它提供了隐式类对象 (cls) ,您可以将静态数据(如已准备好的处理器实例)附加到该对象上,然后通常可以通过普通方法上的 self 实例引用来使用该数据。
我们使用 SOAPpy 0.10.1 的最新发行版(请参阅 参考资料)测试了该端点,该发行版具有许多很棒的新功能,稍后我们将在本专栏中进行讨论。 清单 3是使用该端点的 SOAPpy 客户机。打开一个命令 shell 并为服务器运行 python listing2.py。然后打开另一个 shell 并运行 python listing3.py,该命令将报告正确的响应,形如 Add result: 7.0。
清单 3: 用于求两数之和的 SOAPpy 客户机
import SOAPpy ENDPOINT = "http://localhost:8888/add" ADD_NS = "http://www.topxml.com/" remote = SOAPpy.soAPProxy(ENDPOINT, namespace=ADD_NS) print "Add result:", remote.Add(A=3, B=4)
使用描述
正如我们先前所说的,不仅 XML 中的有效负载是有用的 Web 服务特性,描述也是有用的特性。 清单 4是一个用于添加服务的 WSDL 文件,它是根据 Christopher Dix 的原始文件修改而得到的。它是 WSDL 1.1 版本的。
清单 4. 用于添加服务的 WSDL
清单 5提供了一个为端点用户呈现有用信息的 XSLT 脚本。它是从先前的 developerWorks 文章“WSDL processing with XSLT”(请参阅 参考资料)中所开发的一个转换改编而来的。它使用了许多自由方式(liberty)和快捷方式(尤其是在它处理 WSDL 上下文中的限定名时),但它也许可用于目前使用的大多数 WSDL 1.1 文件。
清单 5. XSLT 脚本
Service summary:
Service summary:
Service "" hosted atOperation "[/b]" message details:通常在 Web 服务本身所在的主机上提供该服务人性化的 WSDL 描述是很方便的。 清单 6是 清单 2的变体,它也完成这一任务。它实际上提供三种
功能: 对于端口 9000 上的 GET 请求:提供该 Web 服务
调用消息的易于理解的描述 对于端口 8888 上的 GET 请求:提供未经处理的 WSDL
文件 对于端口 8888 上的 POST 请求:执行 SOAP 请求。清单 6. 清单 2 的变体#HTTP Listener code for SOAP server import BaseHTTPServer #The processor class is the core of the XSLT API from Ft.Xml.Xslt import Processor #4XSLT uses an InputSource sy
stem for reading XML from Ft.Xml import InputSource SOAP_IMPL_FILE = "add.xsl" WSDL_FILE = "listing4.xml" HTML_VIEW_TRANSFORM = "listing5.xslt" class KafkaSoapHandler(BaseHTTPServer.BaseHTTPRequestHandler): def init(cls): from Ft.Lib import Uri #Set up a processor instance to use cls.processor = Processor.Processor() #Load it with add.xsl add_uri = Uri.OsPathToUri(SOAP_IMPL_FILE, attemptAbsolute=1) transform = InputSource.DefaultFactory.fromUri(add_uri) cls.processor.appendStylesheet(transform) #
Now the processor is prepped with a transform and can be used #over and over for the same transform #Prep for WSDL requests cls.wsdl = open(WSDL_FILE).read() return #Make init() a static method of the class init = classmethod(init) def do_POST(self): clen = self.headers.getheader('content-length') if clen: clen = int(clen) else: print 'POST ERROR: missing content-length' return if self.path != '/add': self.send_error(404) input_body = self.rfile.read(clen) #input_body is the request SOAP envelope and contents response_body = self._run_through_kafka(input_body) #response_body is the response SOAP envelope and contents _send_response(self, 200, 'OK', response_body) return def do_GET(self): #response_body is the WSDL file _send_response(self, 200, 'OK', self.wsdl) return def _run_through_kafka(self, body): #In 4Suite all InputSources have base URIs in case t
hey refer to #other URIs in some way and resolution is
required. #The SOAP messages will not have any such URI references, #So use a dummy base URI source = InputSource.DefaultFactory.fromString(body, "urn:dummy") response = self.processor.run(source) return response class HtmlHandler(BaseHTTPServer.BaseHTTPRequestHandler): def init(cls): from Ft.Lib import Uri #Perform the transform once and store the result processor = Processor.Processor() html_desc_uri = Uri.OsPathToUri(HTML_VIEW_TRANSFORM, attemptAbsolute=1) transform = InputSource.DefaultFactory.fromUri(html_desc_uri) processor.appendStylesheet(transform) wsdl_uri = Uri.OsPathToUri(WSDL_FILE, attemptAbsolute=1) source = InputSource.DefaultFactory.fromUri(wsdl_uri) cls.html_desc = processor.run(source) return #Make init() a static class method init = classmethod(init) def do_GET(self): #response_body is the WSDL file _send_response(self, 200, 'OK', self.html_desc) return #Turn _send_response into a global function #for sharing between the classes def _send_response(handler, code, msg, body): #Prepare a
normal response handler.send_response(200, 'OK') #Send standard HTP headers handler.send_header('Content-type', 'text/html; charset=utf-8') handler.send_header("Connection", "close") handler.send_header("Accept-Ranges", "bytes") handler.send_header('Content-length', len(body)-1) handler.end_headers() #Send the response prepared by the SOAP end point handler.wfile.write(body) return def soap_listener_function(): listen_on_port = 8888 #Set up to run on local machine server_address = ('127.0.0.1', listen_on_port) KafkaSoapHandler.init() httpd = BaseHTTPServer.HTTPServer(server_address, KafkaSoapHandler) print "Listening for GET and POST on port", listen_on_port #Go into a the main event loop httpd.serve_forever() def html_listener_function(): listen_on_port = 9000 #Set up to run on local machine server_address = ('127.0.0.1', listen_on_port) HtmlHandler.init() httpd = BaseHTTPServer.HTTPServer(server_address, HtmlHandler) print "Listening for GET on port", listen_on_port #Go into a the main event loop httpd.serve_forever() return import time from threading import Thread soap_thread = Thread(None, soap_listener_function) html_thread = Thread(None, html_listener_function) soap_thread.start() #Pause before spawning the next thread time.sleep(1) html_thread.start()通过在服务器上定义 do_GET 和 do_POST ,您可以在单个服务器实例上处理 GET 和 POST 请求,但是因为所使用的简单事件循环的性质,您可以使用线程技术在不同端口上进行侦听。这让您同时运行两个服务器实例。线程技术是
方法之一,而使用异步事件处理程序是另一种
方法。Python 2.2 为更轻松地
支持后一种技术而引入了 asyncore 模块,我们在本专栏的
上一篇文章中介绍了这种
方法(请参阅 参考资料)。这一次我们将举例说明线程技术的
用法。关于使用线程技术还是使用异步技术的问题,Python 2.2 文档提出了很好的建议。仅当您的程序很大程度上受
I/O 限制时,[
异步方法才是] 真正实用的。如果您的程序受处理器限制,那么抢先式调度的线程可能是您所真正需要的。但是,网络服务器很少受处理器限制。图 1
显示了易于理解的 Web 服务描述的浏览器视图。
结束语请将这一切都看作实验素材。Kafka 已经相当落伍了 ― 它似乎从 2001 年以来就没有得到过维护,并且它使用了相当差劲的 XSLT 样式(其作者坦率地承认自己是个 XSLT 菜鸟)。但其思想是非常有用的,并且很有价值。只需要作很小的努力就可以将它更新到 SOAP 1.2 并扩展其能力。我们所提供的 WSDL 表示转换也只是
一个起点。也可以将它更新到 WSDL 1.2 并可扩展它以
显示关于 Web 服务的更多信息。还应该更新它以利用
名称空间轴和其它 XSLT
功能以便进行更为正确的处理。XSLT 是
一个沙箱,使用各种语言和环境的开发人员都可以在其中施展身手。Kafka 是由一位坚定的 .NET 开发人员开发的,但我们也可以很快地学会它和利用它。这就是拥有一种既可以处理 XML 也可处理 Web 服务的通用语言(lingua
franca)的威力。我们预计可以使用用于 Web 服务的 XSLT 模块的领域将继续扩展。如果是这样,本文所提供的基本技术可能会促使 Python 程序员们马上使用这些有用的技术。