读webpy源码-请求响应2

续”读webpy源码-请求响应1”,这篇大概讲下respond。


def communicate(self):
       request_seen = False
       try:
           while True:
              req = None
              req = self.RequestHandlerClass(self.server, self)
              req.parse_request()
              if self.server.stats['Enabled']:
                  self.requests_seen += 1
              if not req.ready:
                  return
              request_seen = True
              #响应入口
              req.respond()
              if req.close_connection:
                   return


------------------------> def respond(self): mrbs = self.server.max_request_body_size if self.chunked_read: self.rfile = ChunkedRFile(self.conn.rfile, mrbs) else: cl = int(self.inheaders.get("Content-Length", 0)) ***省略号 self.rfile = KnownLengthRFile(self.conn.rfile, cl) #

响应入口,getway(self)获取网关适配,主要有WSGIGateway_10,WSGIGateway_u0,两者都继承了WSGIGateway,而respond就是WSGIGateway的方法; # WSGIGateway_10有get_environ方法:用于从server里获得关于请求的环境信息(提交方式methon,path请求路径等)

self.server.gateway(self).respond() if (self.ready and not self.sent_headers): self.sent_headers = True self.send_headers() if self.chunked_write: self.conn.wfile.sendall("0\r\n\r\n")
------------------------> def respond(self): #这里是回调了wsgi_app函数,后面接着看下req.server.wsgi_app什么时候加载的。 response = self.req.server.wsgi_app(self.env, self.start_response) try: for chunk in response: if chunk: if isinstance(chunk, unicode): chunk = chunk.encode('ISO-8859-1') self.write(chunk) finally: if hasattr(response, "close"): response.close()
------------------------> application的run方法: def run(self, *middleware): #参数是self.wsgifunc(*middleware),就是后面需要回调的wsgi_app方法。这里看两点:
  1. 如何将回调函数通过上下文填充到WorkThread里的。(看"读webpy源码-请求响应1"篇的注释,已经说明了。)
  2. 回调函数做了什么事情。
def respond(self): #self.req.server.wsgi_app就是runsimple方法的参数func。 response = self.req.server.wsgi_app(self.env, self.start_response) try: for chunk in response: if chunk: if isinstance(chunk, unicode): chunk = chunk.encode('ISO-8859-1') self.write(chunk) finally: if hasattr(response, "close"): response.close()
-------------------------->

下面看下wsgi_app(即func)的定义

def wsgifunc(self, *middleware): def peep(iterator): try: firstchunk = iterator.next() except StopIteration: firstchunk = '' return itertools.chain([firstchunk], iterator) def is_generator(x): return x and hasattr(x, 'next') def wsgi(env, start_resp): self._cleanup() self.load(env) try: if web.ctx.method.upper() != web.ctx.method: raise web.nomethod() result = self.handle_with_processors() if is_generator(result): result = peep(result) else: result = [result] except web.HTTPError, e: result = [e.data] result = web.safestr(iter(result)) status, headers = web.ctx.status, web.ctx.headers start_resp(status, headers) def cleanup(): self._cleanup() yield '' return itertools.chain(result, cleanup()) for m in middleware: wsgi = m(wsgi) #最后的返回,是把wsgi方法返回了。wsgi方法主要是做_cleanup(),load(),handle_with_processors()。 return wsgi

说明:_cleanup()主要是清理老请求环境变量字典; load()主要是根据解析出来的请求信息填充到web.ctx上下文中; handle_with_processors()递归调用应用中实现的处理方法.

def handle_with_processors(self): def process(processors): try: if processors: p, processors = processors[0], processors[1:] return p(lambda: process(processors)) else: return self.handle() except web.HTTPError: raise except (KeyboardInterrupt, SystemExit): raise except: print >> web.debug, traceback.format_exc() raise self.internalerror() return process(self.processors)

以下引用自他人博客,可直链过去看

这个函数挺复杂的,最核心的部分采用了递归实现(我感觉不递归应该也能实现同样的功能)。为了说明清晰,采用实例说明。
前面有提到,初始化application实例的时候,会添加两个处理器到self.processors:
self.add_processor(loadhook(self._load))
self.add_processor(unloadhook(self._unload))
所以,现在的self.processors是下面这个样子的:
self.processors = [loadhook(self._load), unloadhook(self._unload)]
 # 为了方便后续说明,我们缩写一下:
self.processors = [load_processor, unload_processor]
当框架开始执行handle_with_processors的时候,是逐个执行这些处理器的。我们还是来看代码分解,首先简化一下handle_with_processors函数:
def handle_with_processors(self):
  def process(processors):
    try:
      if processors: # 位置2
        p, processors = processors[0], processors[1:]
        return p(lambda: process(processors)) # 位置3
      else:
        return self.handle() # 位置4
    except web.HTTPError:
      raise
    ...
  return process(self.processors) # 位置1
    函数执行的起点是位置1,调用其内部定义函数process(processors)。
    如果位置2判断处理器列表不为空,则进入if内部。
    在位置3调用本次需要执行的处理器函数,参数为一个lambda函数,然后返回。
    如果位置2判断处理器列表为空,则执行self.handle(),该函数真正的调用我们的应用代码(下面会讲到)。
以上面的例子来说,目前有两个处理器:
self.processors = [load_processor, unload_processor]
从位置1进入代码后,在位置2会判断还有处理器要执行,会走到位置3,此时要执行代码是这样的:
return load_processor(lambda: process([unload_processor]))
load_processor函数是一个经过loadhook装饰的函数,因此其定义在执行时是这样的:
def load_processor(lambda: process([unload_processor])):
  self._load()
  return process([unload_processor]) # 就是参数的lambda函数
会先执行self._load(),然后再继续执行process函数,依旧会走到位置3,此时要执行的代码是这样的:
return unload_processor(lambda: process([]))
unload_processor函数是一个经过unloadhook装饰的函数,因此其定义在执行时是这样的:
def unload_processor(lambda: process([])):
  try:
    result = process([]) # 参数传递进来的lambda函数
    is_generator = result and hasattr(result, 'next')
  except:
    self._unload()
    raise

  if is_generator:
    return wrap(result)
  else:
    self._unload()
    return result
现在会先执行process([])函数,并且走到位置4(调用self.handle()的地方),从而得到应用的处理结果,然后再调用本处理器的处理函数self._unload()。
总结一下执行的顺序:
self._load()
  self.handle()
self._unload()
如果还有更多的处理器,也是按照这种方法执行下去,对于loadhook装饰的处理器,先添加的先执行,对于unloadhook装饰的处理器,后添加的先执行。
handle函数
讲了这么多,才讲到真正要调用我们写的代码的地方。在所有的load处理器执行完之后,就会执行self.handle()函数,其内部会调用我们写的应用代码。比如返回个hello, world之类的。self.handle的定义如下:
def handle(self):
  fn, args = self._match(self.mapping, web.ctx.path)
  return self._delegate(fn, self.fvars, args)
这个函数就很好理解了,第一行调用的self._match是进行路由功能,找到对应的类或者子应用,第二行的self._delegate就是调用这个类或者传递请求到子应用。
_match函数
_match函数的定义如下:
def _match(self, mapping, value):
  for pat, what in mapping:
    if isinstance(what, application): # 位置1
      if value.startswith(pat):
        f = lambda: self._delegate_sub_application(pat, what)
        return f, None
      else:
        continue
    elif isinstance(what, basestring): # 位置2
      what, result = utils.re_subm('^' + pat + '$', what, value)
    else: # 位置3
      result = utils.re_compile('^' + pat + '$').match(value)

    if result: # it's a match
      return what, [x for x in result.groups()]
  return None, None
该函数的参数中mapping就是self.mapping,是URL路由映射表;value则是web.ctx.path,是本次请求路径。该函数遍历self.mapping,根据映射关系中处理对象的类型来处理:
    位置1,处理对象是一个application实例,也就是一个子应用,则返回一个匿名函数,该匿名函数会调用self._delegate_sub_application进行处理。
    位置2,如果处理对象是一个字符串,则调用utils.re_subm进行处理,这里会把value(也就是web.ctx.path)中的和pat匹配的部分替换成what(也就是我们指定的一个URL模式的处理对象字符串),然后返回替换后的结果以及匹配的项(是一个re.MatchObject实例)。
    位置3,如果是其他情况,比如直接指定一个类对象作为处理对象。
如果result非空,则返回处理对象和一个参数列表(这个参数列表就是传递给我们实现的GET等函数的参数)。
_delegate函数
从_match函数返回的结果会作为参数传递给_delegate函数:
fn, args = self._match(self.mapping, web.ctx.path)
return self._delegate(fn, self.fvars, args)
其中:
    fn:是要处理当前请求的对象,一般是一个类名。
    args:是要传递给请求处理对象的参数。
    self.fvars:是实例化application时的全局名称空间,会用于查找处理对象。
_delegate函数的实现如下:
def _delegate(self, f, fvars, args=[]):
  def handle_class(cls):
    meth = web.ctx.method
    if meth == 'HEAD' and not hasattr(cls, meth):
      meth = 'GET'
    if not hasattr(cls, meth):
      raise web.nomethod(cls)
    tocall = getattr(cls(), meth)
    return tocall(*args)

  def is_class(o): return isinstance(o, (types.ClassType, type))

  if f is None:
    raise web.notfound()
  elif isinstance(f, application):
    return f.handle_with_processors()
  elif is_class(f):
    return handle_class(f)
  elif isinstance(f, basestring):
    if f.startswith('redirect '):
      url = f.split(' ', 1)[1]
      if web.ctx.method == "GET":
        x = web.ctx.env.get('QUERY_STRING', '')
        if x:
          url += '?' + x
      raise web.redirect(url)
    elif '.' in f:
      mod, cls = f.rsplit('.', 1)
      mod = __import__(mod, None, None, [''])
      cls = getattr(mod, cls)
    else:
      cls = fvars[f]
    return handle_class(cls)
  elif hasattr(f, '__call__'):
    return f()
  else:
    return web.notfound()
这个函数主要是根据参数f的类型来做出不同的处理:
    f为空,则返回302 Not Found.
    f是一个application实例,则调用子应用的handle_with_processors()进行处理。
    f是一个类对象,则调用内部函数handle_class。
    f是一个字符串,则进行重定向处理,或者获取要处理请求的类名后,调用handle_class进行处理(我们写的代码一般是在这个分支下被调用的)。
    f是一个可调用对象,直接调用。
    其他情况返回302 Not Found.
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值