Bottle源码阅读(2) WSGIHandler

WSGI Handler

def WSGIHandler(environ, start_response):
    """The bottle WSGI-handler."""
    global request
    global response
    request.bind(environ)
    response.bind()
    try:
        handler, args = match_url(request.path, request.method)
        if not handler:
            raise HTTPError(404, "Not found")
        output = handler(**args)
    except BreakTheBottle as shard:
        output = shard.output
    except Exception as exception:
        response.status = getattr(exception, 'http_status', 500)
        errorhandler = ERROR_HANDLER.get(response.status, error_default)
        try:
            output = errorhandler(exception)
        except:
            output = "Exception within error handler! Application stopped."

        if response.status == 500:
            request._environ['wsgi.errors'].write("Error (500) on '%s': %s\n" % (request.path, exception))

    db.close() # DB cleanup

    if hasattr(output, 'read'):
        fileoutput = output
        if 'wsgi.file_wrapper' in environ:
            output = environ['wsgi.file_wrapper'](fileoutput) # 注意文件不能直接返回,必须要用file_wrapper进行包装。
        else:
            output = iter(lambda: fileoutput.read(8192), '')  #将文件变成一个每个元素4kb的list
    elif isinstance(output, str):
        output = [output]

    for c in response.COOKIES.values():
        response.header.add('Set-Cookie', c.OutputString())

    status = '%d %s' % (response.status, HTTP_CODES[response.status])
    start_response(status, list(response.header.items()))
    return output

这段代码有40行,这段代码就是Bottle的与WSGIServer通信的核心。

要看懂这段代码,就不得不了解WSGI的实现了。WSGI是一个协议,该协议定义了WSGI的实现必须是一个可以调用的对象,在Python中也就是必须要有**Object.__call__()**这个方
法,这个callable对象还必须有两个条件:

  1. 接受两个参数
    • 一个包含CGI参数字典
    • 一个发送HTTP头、HTTP状态码、HTTP内容的回调函数,这个函数有服务器提供
  2. 返回一个可以迭代的包含响应内容的字符串给服务器

有了这两点,上面这段代码就不难看懂了。

environ就是包含CGI参数地字典,而start_response就是那个回调函数,而WSGIHandler这个函数就是要交给WSGIServer来调用。

这个handler里处理了request和response,也就是当有一个用户来访问时,WSGIServer调用WSGIHandler,然后request和response绑定该handler。

output=[output]

这就是将字符串变成一个可迭代的对象。

Request

class Request(threading.local):
    """ Represents a single request using thread-local namespace. """

    def bind(self, environ):
        """ Binds the enviroment of the current request to this request handler """
        self._environ = environ
        self._GET = None
        self._POST = None
        self._GETPOST = None
        self._COOKIES = None
        self.path = self._environ.get('PATH_INFO', '/').strip()
        if not self.path.startswith('/'):
            self.path = '/' + self.path

    @property
    def method(self):
        ''' Returns the request method (GET,POST,PUT,DELETE,...) '''
        return self._environ.get('REQUEST_METHOD', 'GET').upper()

    @property
    def query_string(self):
        ''' Content of QUERY_STRING '''
        return self._environ.get('QUERY_STRING', '')

    @property
    def input_length(self):
        ''' Content of CONTENT_LENGTH '''
        try:
            return int(self._environ.get('CONTENT_LENGTH', '0'))
        except ValueError:
            return 0

    @property
    def GET(self):
        """Returns a dict with GET parameters."""
        if self._GET is None:
            raw_dict = parse_qs(self.query_string, keep_blank_values=1)
            self._GET = {}
            for key, value in raw_dict.items():
                if len(value) == 1:
                    self._GET[key] = value[0]
                else:
                    self._GET[key] = value
        return self._GET

    @property
    def POST(self):
        """Returns a dict with parsed POST data."""
        if self._POST is None:
            raw_data = cgi.FieldStorage(fp=self._environ['wsgi.input'], environ=self._environ)
            self._POST = {}
            if raw_data:
                for key in raw_data:
                    if isinstance(raw_data[key], list):
                        self._POST[key] = [v.value for v in raw_data[key]]
                    elif raw_data[key].filename:
                        self._POST[key] = raw_data[key]
                    else:
                        self._POST[key] = raw_data[key].value
        return self._POST

    @property
    def params(self):
        ''' Returns a mix of GET and POST data. POST overwrites GET '''
        if self._GETPOST is None:
            self._GETPOST = dict(self.GET)
            self._GETPOST.update(dict(self.POST))
        return self._GETPOST

    @property
    def COOKIES(self):
        """Returns a dict with COOKIES."""
        if self._COOKIES is None:
            raw_dict = Cookie.SimpleCookie(self._environ.get('HTTP_COOKIE',''))
            self._COOKIES = {}
            for cookie in raw_dict.values():
                self._COOKIES[cookie.key] = cookie.value
        return self._COOKIES

这个在WSGI中也属于协议的一部分,是必须要实现的。需要注意的这里继承了threading.local这个类,那么这个类是干什么的呢?注释中写到,使用的是thread-local的命名空间。文档中写到threading-local的数据是具有线程特性的。那么该类的所有属性都是具有线程特性的即Thread-local data。wiki上解释为一个线程需要用到全局变量,那么就需要用到TLS。这些函数都将会在Server中调用,在绑定之后。

Response

class Response(threading.local):
    """ Represents a single response using thread-local namespace. """

    def bind(self):
        """ Clears old data and creates a brand new Response object """
        self._COOKIES = None

        self.status = 200
        self.header = HeaderDict()
        self.content_type = 'text/html'
        self.error = None

    @property
    def COOKIES(self):
        if not self._COOKIES:
            self._COOKIES = Cookie.SimpleCookie()
        return self._COOKIES

    def set_cookie(self, key, value, **kargs):
        """ Sets a Cookie. Optional settings: expires, path, comment, domain, max-age, secure, version, httponly """
        self.COOKIES[key] = value
        for k in kargs:
            self.COOKIES[key][k] = kargs[k]

    def get_content_type(self):
        '''Gives access to the 'Content-Type' header and defaults to 'text/html'.'''
        return self.header['Content-Type']
        
    def set_content_type(self, value):
        self.header['Content-Type'] = value
        
    content_type = property(get_content_type, set_content_type, None, get_content_type.__doc__)

作者是真喜欢@property这个装饰器啊,这个类同样是一个必须要实现的类。因为在WSGIHandler中必须要实现处理Request和Response。
bottlelogic
这就是整个的逻辑关系,application与WSGIServer通信,WSGIServer调用WSGIHandler来处理,WSGHandler在调用响应Request类和Response类,将Response返回给WSGIServer,WSGIServer再将结果返回给Application就是那个通信的Application。

转载于:https://www.cnblogs.com/zhuozi/p/7146947.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值