• Python WSGI开发随笔


      本文记录学习WSGI时的一些知识点,值得后续学习中注意。

      wsgi应用接口只要是一个可调用的对象就行,这个可调用的对象需要:

    1. 接受两个位置参数:

      a. 包含CGI形式变量的字典;

      b. 应用调用的回调函数,该回调函数的作用是将HTTP响应的状态码和header返回给server。

    2. 将响应body部分的内容作为包裹在一个可迭代的对象中的(若干)字符串。

    说明:

      1. application 的第一个参数env是一个字典,里面包含了CGI形式的环境变量,该字典是由server基于客户请求填充。

      2. headers在构建的时候,必须遵循以下规则:

        [(Header name1, Header value1), (Header name2, Header value2),]
       响应header和响应HTTP状态码通过应用的第二个参数即回调函数发回给server。

      3.body在构建的时候,必须遵循以下规则:
        [response_body]
      即响应body必须被包裹在可迭代的对象中,同时通过return 语句返回给server.

    下面是wsgi.org官方教程中得到一个例子:

      注意environ参数的含义,start_response函数的作用,response_headers和status是由谁返回给server的,response_body需要什么样的形式,如何返回给server等。

    from wsgiref.simple_server import make_server
    
    def application( environ, start_response):
        # response_body in a list
        response_body = ['%s:%s' %(key, value)
            for key, value in sorted(environ.items())]
        response_body = '
    '.join(response_body)
        response_body = ['The Begining
    ',
                            '*' * 30 + '
    ',
                            response_body,
                            '
    ' + '*' * 30 ,
                            '
    The End']
        
        content_length = 0
        for s in response_body:
            content_length += len(s)
        
       #status code and response_headers
        status = '200 OK'
        response_headers =[('Content-Type', 'text/plain'),
            ('Content-Length', str(content_length))]
        # use callback function to send back status code 
        # and response headers
        start_response(status, response_headers)
        # return response body thruogh return statement
      return response_body
    
    
    httpd = make_server('localhost',8051,application)
    httpd.handle_request() 
    

      

    应用端

    HELLO_WORLD = b"Hello world!
    "
    
    def simple_app(environ, start_response):
        """Simplest possible application object"""
        status = '200 OK'
        response_headers = [('Content-type', 'text/plain')]
        start_response(status, response_headers)
        return [HELLO_WORLD]
    
    class AppClass:
        """Produce the same output, but using a class
    
        (Note: 'AppClass' is the "application" here, so calling it
        returns an instance of 'AppClass', which is then the iterable
        return value of the "application callable" as required by
        the spec.
    
        If we wanted to use *instances* of 'AppClass' as application
        objects instead, we would have to implement a '__call__'
        method, which would be invoked to execute the application,
        and we would need to create an instance for use by the
        server or gateway.
        """
    
        def __init__(self, environ, start_response):
            self.environ = environ
            self.start = start_response
    
        def __iter__(self):
            status = '200 OK'
            response_headers = [('Content-type', 'text/plain')]
            self.start(status, response_headers)
            yield HELLO_WORLD
    

    服务/网关端

    import os, sys
    
    enc, esc = sys.getfilesystemencoding(), 'surrogateescape'
    
    def unicode_to_wsgi(u):
        # Convert an environment variable to a WSGI "bytes-as-unicode" string
        return u.encode(enc, esc).decode('iso-8859-1')
    
    def wsgi_to_bytes(s):
        return s.encode('iso-8859-1')
    
    def run_with_cgi(application):
        environ = {k: unicode_to_wsgi(v) for k,v in os.environ.items()}
        environ['wsgi.input']        = sys.stdin.buffer
        environ['wsgi.errors']       = sys.stderr
        environ['wsgi.version']      = (1, 0)
        environ['wsgi.multithread']  = False
        environ['wsgi.multiprocess'] = True
        environ['wsgi.run_once']     = True
    
        if environ.get('HTTPS', 'off') in ('on', '1'):
            environ['wsgi.url_scheme'] = 'https'
        else:
            environ['wsgi.url_scheme'] = 'http'
    
        headers_set = []
        headers_sent = []
    
        def write(data):
            out = sys.stdout.buffer
    
            if not headers_set:
                 raise AssertionError("write() before start_response()")
    
            elif not headers_sent:
                 # Before the first output, send the stored headers
                 status, response_headers = headers_sent[:] = headers_set
                 out.write(wsgi_to_bytes('Status: %s
    ' % status))
                 for header in response_headers:
                     out.write(wsgi_to_bytes('%s: %s
    ' % header))
                 out.write(wsgi_to_bytes('
    '))
    
            out.write(data)
            out.flush()
    
        def start_response(status, response_headers, exc_info=None):
            if exc_info:
                try:
                    if headers_sent:
                        # Re-raise original exception if headers sent
                        raise exc_info[1].with_traceback(exc_info[2])
                finally:
                    exc_info = None     # avoid dangling circular ref
            elif headers_set:
                raise AssertionError("Headers already set!")
    
            headers_set[:] = [status, response_headers]
    
            # Note: error checking on the headers should happen here,
            # *after* the headers are set.  That way, if an error
            # occurs, start_response can only be re-called with
            # exc_info set.
    
            return write
    
        result = application(environ, start_response)
        try:
            for data in result:
                if data:    # don't send headers until body appears
                    write(data)
            if not headers_sent:
                write('')   # send headers now if body was empty
        finally:
            if hasattr(result, 'close'):
                result.close()
    

      

    中间件

    from piglatin import piglatin
    
    class LatinIter:
    
        """Transform iterated output to piglatin, if it's okay to do so
    
        Note that the "okayness" can change until the application yields
        its first non-empty bytestring, so 'transform_ok' has to be a mutable
        truth value.
        """
    
        def __init__(self, result, transform_ok):
            if hasattr(result, 'close'):
                self.close = result.close
            self._next = iter(result).__next__
            self.transform_ok = transform_ok
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.transform_ok:
                return piglatin(self._next())   # call must be byte-safe on Py3
            else:
                return self._next()
    
    class Latinator:
    
        # by default, don't transform output
        transform = False
    
        def __init__(self, application):
            self.application = application
    
        def __call__(self, environ, start_response):
    
            transform_ok = []
    
            def start_latin(status, response_headers, exc_info=None):
    
                # Reset ok flag, in case this is a repeat call
                del transform_ok[:]
    
                for name, value in response_headers:
                    if name.lower() == 'content-type' and value == 'text/plain':
                        transform_ok.append(True)
                        # Strip content-length if present, else it'll be wrong
                        response_headers = [(name, value)
                            for name, value in response_headers
                                if name.lower() != 'content-length'
                        ]
                        break
    
                write = start_response(status, response_headers, exc_info)
    
                if transform_ok:
                    def write_latin(data):
                        write(piglatin(data))   # call must be byte-safe on Py3
                    return write_latin
                else:
                    return write
    
            return LatinIter(self.application(environ, start_latin), transform_ok)
    
    
    # Run foo_app under a Latinator's control, using the example CGI gateway
    from foo_app import foo_app
    run_with_cgi(Latinator(foo_app))
    

      

  • 相关阅读:
    Info.plist的秘密(raywenderlich笔记)
    我所理解的性能测试中负载测试与压力测试的区别
    seo优化
    localStorage,sessionStorage那些事儿
    浏览器cookie那些事儿
    JavaScript--Ajax请求
    javascript-理解原型、原型链
    React Native:从入门到原理
    知道这 20 个正则表达式,能让你少写 1,000 行代码
    程序猿进化必读:让App的运行速度与响应速度趋于一流(iOS)
  • 原文地址:https://www.cnblogs.com/Security-Darren/p/4085592.html
Copyright © 2020-2023  润新知