本文是建立在分析CherryPy的Hello World及背后的源码基础上的,旨在分析支持Hello World运行的CherryPy框架。
- 概述
CherryPy 3.2.2作为Web Application Server,支撑着CherryPy的Application的运行。相关的背景资料可以参考《关于cherrypy和wsgidav的一些问题》。
- Hello World
源码如下:
- CherryPy框架
支撑运行Hello World的CherryPy的框架大致如下图所示,图中的节点名称是CherryPy中的类名。
当一个请求发生时,首先到达的是CPWSGIServer,CPWSGIServer基于socket,win32下对应的底层支持module是process/win32.py。由于CherryPy支持WSGI接口的应用,故CPWSGIServer也能支撑WSGI应用。
CPWSGIServer会将每个请求封装成一个HTTPConnection,然后扔到队列Queue里面,由ThreadPool中的WorkerThread来处理,处理方式很简单,就是ThreadPool启动多个WorkerThread,每个WorkerThread去Queue中取HTTPConnection,然后处理之。CherryPy中,可以设置WorkerThread的个数。
ThreadPool模块和_cp开头的module的交互是通过_cpserver.Server,它只是一个Adapter,包装了底层的Server类。HTTPConnection会将消息转发到_cptree.Tree,该类中记载了站点的相对路径(比如“/”)和对应的Application之间的对应关系,那么它会跟据不同的URL请求转到对应的Application中。
Application中则封装了用户定义的类HelloWorld对象,故在需要返回数据给客户端时,调用到HelloWorld中。
CherryPy是Python的一个Web框架,通过MethodDispatcher内建了对REST的支持,而且使用非常方便。
示例
首先,我们需要有一个符合REST风格的资源(Resource)。通过Python对象来定义:
import cherrypy class ProgSnaps(object): exposed=True def GET(self): return "You access the REST api /ProgSap/GET"
接着,将配置里默认的Dispatcher修改为支持REST的MethodDispatcher:
conf = {'/':{'request.dispatch':cherrypy.dispatch.MethodDispatcher()} }
最后,为CherryPy添加到资源的映射,启动服务器就可以通过url:http://localhost:8080/api/ProgSnaps 来访问了。
cherrypy.tree.mount(ProgSnaps(),"/api/ProgSnaps",conf)
注意事项
- 作为资源的Python对象必须是整个对象都exposed,通过设置一个对象的属性来实现,比如在ProgSnaps中设定exposed=true。非REST的情况下,都是在要暴露的方法上面添加@cherrypy.expose属性。
- REST的方法名必须都是大写的,比如GET,PUT,POST和DELETE。其它非大写的方法也可以定义在资源对象中,但是无法通过url访问到,可以作为实现REST方法(GET等)的辅助方法存在。比如例子中的getContent方法。
- 不需要自己定义http的allow头信息了,CherryPy根据资源对象中暴露的REST方法自动生成。比如本例中我们在ProgSnaps中定义了GET和PUT,那么最后http的头中会有这样的信息:
Allow:GET, HEAD, UPDATE
注:HEAD是默认添加的。REST总是允许访问HEAD信息。
完整代码
import cherrypy class ProgSnaps(object): exposed=True def GET(self): #return "You access the REST api /ProgSap/GET" return self.getContent("GET") def UPDATE(self): return "You have accessed the REST api /ProgSap/UPDATE" def getContent(self,method): return "You access the REST api /ProgSap/"+method conf = { '/':{'request.dispatch':cherrypy.dispatch.MethodDispatcher()} } if __name__ == '__main__': cherrypy.tree.mount(ProgSnaps(),"/api/ProgSnaps",conf) cherrypy.engine.start() cherrypy.engine.block()