Twisted的官方文档上对于putChild的解释是“Register a static child”(点击打开链接),即为当前资源节点注册一个静态子资源节点。实际上,Resource类中的putChild实现的是IResource接口中的putChild方法(点击打开链接)。
Resource类中还有一个getChild方法,官方文档的解释是“Retrieve a 'child' resource from me. Implement this to create dynamic resource generation -- resources which are always available may be registered with self.putChild()”(点击打开链接)。这两句话说明getChild的作用是从当前资源节点获取子资源节点。但是,与putChild不同,getChild方法能够创建动态资源。
理解putChild和getChild的关键在于理解“静态资源”和“动态资源”两个概念。下面先看一个书上(Twisted Network Programming Essentials)的例子:
from twisted.internet import reactor
from twisted.web.resource import Resource, NoResource
from twisted.web.server import Site
from calendar import calendar
class YearPage(Resource):
def __init__(self, year):
Resource.__init__(self)
self.year = year
def render_GET(self, request):
return "%s
" % (calendar(self.year), )
class CalendarHome(Resource):
def getChild(self, name, request):
if name == "":
return self
if name.isdigit():
return YearPage(int(name))
else:
return NoResource()
def render_GET(self, request):
return "Welcome to the calendar server!"
root = CalendarHome()
factory = Site(root)
reactor.listenTCP(8000, factory)
reactor.run()
现在把上面的代码稍加改写,如下所示:
from twisted.internet import reactor
from twisted.web.resource import Resource, NoResource
from twisted.web.server import Site
from calendar import calendar
class HelpPage(Resource):
isLeaf = True
def render_GET(self, request):
return "help"
class YearPage(Resource):
def __init__(self, year):
Resource.__init__(self)
self.year = year
self.putChild("help", HelpPage())
def render_GET(self, request):
return "%s
" % (calendar(self.year), )
class RoomPage(Resource):
isLeaf = True
def render_GET(self, request):
return "room"
class HomePage(Resource):
def __init__(self):
Resource.__init__(self)
self.putChild("room", RoomPage())
def render_GET(self, request):
return "home"
class CalendarHome(Resource):
def __init__(self):
Resource.__init__(self)
self.putChild("home", HomePage())
self.putChild("year", YearPage(2016))
def getChild(self, name, request):
if name == "":
return self
return NoResource()
def render_GET(self, request):
return "Welcome to the calendar server!"
root = CalendarHome()
factory = Site(root)
reactor.listenTCP(8000, factory)
reactor.run()
再对上面的代码中CalendarHome的构造函数稍作修改:
class CalendarHome(Resource):
def __init__(self):
Resource.__init__(self)
self.putChild("", HomePage())
self.putChild("year", YearPage(2016))
def getChild(self, name, request):
if name == "":
return self
return NoResource()
def render_GET(self, request):
return "Welcome to the calendar server!"
需要注意的是,此时访问localhost:8000/room会出现404错误。实际上,这是CalendarHome的getChild函数在作怪。因为getChild函数中写得清清楚楚,当name不为“”时,一律返回NoResource。如果把getChild代码删掉,再访问localhost:8000/room呢?答案依然是404。这是因为CalendarHome的getChild方法继承自Resource类,而Resource类中的getChild源码如下:
161 def getChild(self, path, request):
162 """
163 Retrieve a 'child' resource from me.
164
165 Implement this to create dynamic resource generation -- resources which
166 are always available may be registered with self.putChild().
167
168 This will not be called if the class-level variable 'isLeaf' is set in
169 your subclass; instead, the 'postpath' attribute of the request will be
170 left as a list of the remaining path elements.
171
172 For example, the URL /foo/bar/baz will normally be::
173
174 | site.resource.getChild('foo').getChild('bar').getChild('baz').
175
176 However, if the resource returned by 'bar' has isLeaf set to true, then
177 the getChild call will never be made on it.
178
179 Parameters and return value have the same meaning and requirements as
180 those defined by L{IResource.getChildWithDefault}.
181 """
182 return NoResource("No such child resource.")