Twisted网络编程必备(4)

来源:互联网 发布:如何购买阿里云空间 编辑:程序博客网 时间:2024/05/17 03:11


转自:http://www.yybug.com/read-htm-tid-15324.html

4.0 WEB服务器

 

即使是很保守的说,现在的很多软件是基于WEB开发的。人们将大量时间花费在WEB浏览器上面,包括阅读HTML页面、电子邮件、管理日志、进入数据库的记录、更新Wiki页面和写weblog。

即使你不打算写严格的WEB应用,WEB界面也更加容易提供适合于跨平台的UI。在你的应用中包含轻量级的WEB服务器将会提供更多的附属功能。这一章将会展示如何使用Twisted开发一个WEB服务器,并介绍你一些构建WEB应用的方法。当然还提供了HTTP代理服务器。

这一章提供了一些HTTP协议的常识。这些一些构建WEB服务器所必须的知识。实际上,本书所涉及的HTTP知识还远远不够,还需要如《HTTP: The Definitive Guide》等等的书,还有不可替代的HTTP的RFC文档RFC2616(http://www.faqs.org/rfcs/rfc2616.html)。

 

4.1 响应HTTP请求

 

HTTP是一个简单的协议接口。客户端发送请求,服务器端发送响应,然后关闭连接。你可以自己实现一个HTTP的Protocol来练习接收连接,读取请求,并发送HTTP格式的响应。

 

4.1.1 下面如何做?

 

每个HTTP请求开始于单行的HTTP方法,紧接着是URL,然后是HTTP版本。随后是一些行的头字段。一个空行标志着头字段的结束。头字段后面就是请求主体,例如提交的HTML表单数据。

如下是一个HTTP请求的例子。这个请求询问服务器通过GET方法获取www.example.com/index.html资源,并使用HTTP版本1.1。

GET /index.html HTTP/1.1

Host: www.example.com

服务器响应的第一行告知客户端响应的HTTP版本和状态码。有如请求一样,响应包含了头字段,并用空行隔开消息主体。如下是HTTP响应的例子:

HTTP/1.1 200 OK

Content-Type: text/plain

Content-Length: 17

Connection: Close

 

Hello HTTP world!

设置简单的HTTP服务器,需要写一个Protocol允许客户端连接。查找空行标志着头字段的结束。然后发送HTTP响应,例子4-1展示了简单的HTTP实现。

 

复制代码
代码
from twisted.protocols import basicfrom twisted.internet import protocol,reactorclass HttpEchoProtocol(basic,LineReceiver): def __init__(self): self.lines=[] self.gotRequest=False def lineReceived(self,line): self.lines.append(line) if not line and not self.gotRequest: self.sendResponse() self.gotRequest=True def sendResponse(self): responseBody="You said: \r\n\r\n"+"\r\n".join(self.lines) self.sendLine("HTTP/1.0 200 OK") self.sendLine("Content-Type: text/plain") self.sendLine("Content-Length: %i"%len(responseBody)) self.sendLine("") self.transport.write(responseBody) self.transport.loseConnection()f=protocol.ServerFactory()f.protocol=HttpEchoProtocolreactor.listenTCP(8000,f)reactor.run()
复制代码

 

 

运行webecho.py脚本启动服务器。你可以看到服务器运行在http://localhost:8000。你可以获得请求的回显,即原请求的报文。

 

4.1.2 它们如何工作?

 

HTTPEchoProtocol懂得如何响应每一个请求。从客户端收到的数据将会存储在self.lines中。当看到一个空行时,可以知道头字段结束了。它发送回一个HTTP响应。第一行包含了HTTP版本和状态码,在这种情况下,200是成功("OK"是附加的便于阅读的状态码描述)。下一对行是Content-Type和Content-Length头字段,用于告知客户端内容格式和长度。HTTPEchoProtocol发送一个空行来结束头字段,然后发送响应主体,回显客户端请求报文。

4.2 解析HTTP请求

 

HTTPEchoProtocol类提供了有趣的HTTP入门,但是距离使用功能还很遥远。它并不去分析请求头和资源位置,HTTP方法也只支持一种。如果想要建立一个真正的WEB服务器,你可以用一种更好的方式来解析和响应请求。下面的实现展示这些。

 

4.2.1 下面如何做?

 

写twisted.web.http.Request的子类并重载process方法来处理当前请求。Request对象已经包含了process需要调用的HTTP请求所有信息,所以只需要决定如何响应。例子4-2展示了如何运行基于http.Request的HTTP服务器。

 

复制代码
代码
from twisted.web import httpclass MyRequestHandler(http.Request): pages={ '/':'<h1>Home</h1>Home Page', '/test':'<h1>Test</h1>Test Page', } def process(self): if self.pages.has_key(self.path): self.write(self.pages[self.path]) else: self.setResponseCode(http.NOT_FOUND) self.write("<h1>Not Found</h1>Sorry, no such page.") self.finish()class MyHttp(http.HTTPChannel): requestFactory=MyRequestHandlerclass MyHttpFactory(http.HTTPFactory): protocol=MyHttpif __name__=='__main__': from twisted.internet import reactor reactor.listenTCP(8000,MyHttpFactory()) reactor.run()
复制代码

 

 

运行requesthandler.py将会在8000端口启动一个WEB服务器。可以同时阅览主页(http://localhost:8000/)和测试页/test(http://localhost:8000/test)。如果你指定了想要访问其他页面,将会得到错误信息。

4.2.2 它们如何工作?

 

http.Request类解析了进入的HTTP请求并提供了制造响应的接口。在例子4-2中,MyRequestHandler是http.Request的一个子类提供了process方法。process方法将会被请求调用并接收。有责任在产生一个响应之后调用self.finish()来指出响应完成了。MyRequestHandler使用path属性来寻找请求路径。并在pages字典中指定匹配路径。如果匹配成功,MyRequestHandler使用write方法发送响应文本到响应。

注意write仅在写入响应的部分实体主体时才使用,而不是在生成原始HTTP响应时。setResponseCode方法可以用于改变HTTP状态码。twisted.web.http模块提供了所有HTTP状态码的定义,所以可用http.NOT_FOUND来代替404错误。

Tip:Request.setResponseCode带有一个可选的第二个参数,一个易读的状态消息。你可以感觉到很方便于twisted.web.http模块包含了内置列表描述普通的状态码,也就是缺省使用的。

Request类也提供setHeader方法来添加响应头字段。MyRequestHandler使用setHeader来设置Content-Type头为text/html,这个设置告诉浏览器对响应主体使用HTML格式。

twisted.web.http模块提供了两种附加类允许将Request的子类转换成功能性的WEB服务器。HTTPChannel类是一个Protocol,可以创建Request对象来应付每个连接。创建HTTPChannel时使用你的Request子类,重载requestFactory类属性。HTTPFactory是一个ServerFactory,添加附加特性,包含日志方法加上Request对象,这种日志格式包括Apache和其他多种日志格式。

4.3 处理POST数据和HTML表单

 

前面的实验展示了通过客户端请求生成静态HTML。这个实验展示了允许书写代码控制响应的生成,并且处理HTML表单提交的数据。

 

4.3.1 下面如何做?

 

写一个函数来处理Request对象并产生响应。设置字典来映射每一个可用路径到WEB站点来让函数出路路径请求。使用Request.args字典存取提交的HTML表单数据。例子4-3展示了生成单页HTML表单的WEB服务器,另一个页面是通过表单数据显示的。

 

复制代码
代码
from twisted.web import httpdef renderHomePage(request): colors='red','blue','green' flavors='vanilla','chocolate','strawberry','coffee' request.write("""<html><head> <title>Form Test</title></head><body> <form action="posthandler" method="POST"> Your Name: <p> <input type="text" name="name"> </p> What's your favorite color? <p>""") for color in colors: request.write( "<input type='radio' name='color' value='%s'>%s<br/>"%( color,color.capitalize())) request.write(""" </p> What kinds of ice cream do you like? <p> """) for flavor in flavors: request.write( "<input type='checkbox' name='flavor' value='%s'>%s<br/>"%( flavor,flavor.capitalize())) request.write(""" </p> <input type='submit'/> </form></body></html>""") request.finish()def handlePost(request): request.write(""" <html><head><title>Posted Form Datagg</title> </head> <body> <h1>Form Data</h1> """) for key,values in request.args.items(): request.write("<h2>%s</h2>"%key) request.write("<ul>") for value in values: request.write("<li>%s</li>"%value) request.write("</ul>") request.write(""" </body></html> """) request.finish()class FunctionHandleRequest(http.Request): pageHandlers={ '/':renderHomePage, '/posthandler':handlePost, } def process(self): self.setHeader("Content-Type","text/html") if self.pageHandlers.has_key(self.path): handler=self.pageHandlers[self.path] handler(self) else: self.setResponseCode(http.NOT_FOUND) self.write("<h1>Not Found</h1>Sorry, no such page.") self.finish()class MyHttp(http.HTTPChannel): requestFactory=FunctionHandledRequestclass MyHttpFactory(http.HTTPFactory): protocol=MyHttpif __name__=='__main__': from twisted.internet import reactor reactor.listenTCP(8000,MyHttpFactory()) reactor.run()
复制代码

 

 

运行formhandler.py脚本。将会在8000端口运行WEB服务器。进入http://localhost:8000可以找到表单主页。按照如下填写一些字段信息。

然后点击提交按钮,你的浏览器将会发送表单数据到页面formhandler使用HTTP的POST请求。当它接受到表单数据时,formhandler回展示提交过的字段和值。

4.3.2 它们是如何工作的?

 

例子4-3定义了两个函数来处理请求,renderHomePage和handlePost。FunctionHandleRequest是Request的子类,其属性pageHandler定义了路径映射功能。process方法查找路径,并尝试在pageHandlers中匹配路径。如果匹配成功,则FunctionHandleRequest传递自身到匹配函数,并且由对方负责处理;如果匹配失败,则返回404 Not Found响应。

renderHomePage函数设置处理器到/,站点的根路径。它生成的HTML表单将会提交数据到页面/formhandler。这个处理器函数/formhandler是handlePost,将会响应页面列表并提交数据。handlePost遍历值Request.args,这个字典属性包含了请求提交的所有数据。

Tip:在这种情况下,发送的表单数据在HTTP POST请求的主体中。当请求发送HTTP GET时,Request.args将会包含所有提交的URI查询字段值。你可以修改这个行为,通过改变表单生成器renderHomePage的method属性,从POST到GET,重启服务器,就可以重新提交表单。

一个HTML表单可以有多个字段具有相同的名字。例如,表单4-3中允许选中多个复选框,所有的名字都是flavor。不像很多其他的框架,http.Request并不对你隐藏什么:代替了映射字段名到字符串,Request.args映射每个字段值到列表。如果你知道要取哪一个值,那么可以只获取列表的第一个值。

0 0
原创粉丝点击