WSGI

  • Post author:
  • Post category:其他




WSGI



什么是WSGI


WSGI

:全称是Web Server Gateway Interface,WSGI协议是描述web server如何与web application通信的规范。server和application的规范在PEP 3333中有具体描述。要实现WSGI协议,必须同时实现web server和web application,当前运行在WSGI协议之上的web框架有Torando,Flask,Django等。



WSGI协议主要包括server、application和middlewares部分

WSGI协议其实是定义了一种server与application解耦的规范,即可以有多个实现WSGI server的服务器,也可以有多个实现WSGI application的框架,可以选择任意的server和application组合实现自己的web应用。

例如:uWSGI和Gunicorn都是实现了WSGI server协议的服务器;Django,Flask是实现了WSGI application协议的web框架,可以根据项目实际情况搭配使用。



WSGI server


WSGI server

负责从客户端接收请求,将request转发给application,将application返回的response返回给客户端;



WSGI application


WSGI application

接收由server转发的request,处理请求,并将处理结果返回给server。



中间件

application中可以包括多个

中间件(middlewares)

,这些中间件需要同时实现server与application,因此可以在WSGI服务器与WSGI应用之间起调节作用:对服务器来说,中间件扮演应用程序,对应用程序来说,中间件扮演服务器。



实现简单的WSGI

实现WSGI协议必须要有wsgi server和application,一般application需要我们自己实现。

有很多符合WSGI规范的服务器,这里使用python内置的wsgiref模块,它是用纯Python编写的WSGI服务器的参考实现。



application是一个函数

from wsgiref.simple_server import make_server


# 创建app,接收两个参数environ,start_response
# 第一个参数environ就是一个字典,里面存着环境变量,里面的内容和CGI的环境变量基本一致。 
# 第二个参数start_response是一个函数,用于输出HTTP的响应头,start_response一般有两个参数:第一个参数是就是状态码,比如200 OK,302 Found之类的。第二个参数则是HTTP首部的其他信息,是一个多个元组构成的list。每个首部字段,都放置到一个元组之中。
# 最终的返回值,就是HTTP响应的实体部分了,必须是一个可迭代对象(python3中需要编码为bytes类型)。
def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return ['<h1>Hello, web!</h1>'.encode("utf-8")]


# 创建一个服务器,IP地址为空,端口是8000,处理函数是application:
httpd = make_server('127.0.0.1', 8890, application)
# 开始监听HTTP请求:
httpd.serve_forever()

在这里插入图片描述



application是一个对象

from wsgiref.simple_server import make_server

# 可调用对象是一个 Application对象
class Application(object):
    def __init__(self):
        pass

    def __call__(self, environ, start_response):
        status = '200 OK'
        response_headers = [('Content-type', 'text/plain')]
        start_response(status, response_headers)
        return ["Hello world!\n".encode("utf-8")]


# 创建一个服务器,IP地址为空,端口是8000,处理函数是application:
app = Application()
httpd = make_server('127.0.0.1', 8890, app)
# 开始监听HTTP请求:
httpd.serve_forever()



实现简单的路由

import re
from wsgiref.simple_server import make_server


def application(environ, start_response):
    method = environ["REQUEST_METHOD"].lower()
    path = environ["PATH_INFO"].lstrip("/")
    if path == "favicon.ico":
        start_response('200 OK', [('Content-Type', 'text/html')])
        return ["".encode("utf-8")]
    res = None
    for url, controller in urls:
        if re.match(url, path):
            if isinstance(controller, type) and hasattr(controller, method):
                res = getattr(controller(), method)()
            else:
                start_response('502 Bad Gateway', [('Content-Type', 'text/html')])
                return ["Invalid method".encode("utf-8")]

    start_response('200 OK', [('Content-Type', 'text/html')])
    return [res.encode("utf-8")]


class HelloController(object):
    def get(self, *args, **kwargs):
        return "Hello world"

    def post(self, *args, **kwargs):
        pass


urls = [
    (r"hello", HelloController)
]

# 创建一个服务器,IP地址为空,端口是8000,处理函数是application:
httpd = make_server('127.0.0.1', 8890, application)
# 开始监听HTTP请求:
httpd.serve_forever()

在这里插入图片描述



支持HTTPS的web server

wsgiref不支持https,可以借助第三方模块gevent和eventlet都可以完成:

from eventlet import wsgi
import eventlet

wsgi.server(eventlet.wrap_ssl(eventlet.listen(('', 8443)),
                              certfile='cert.crt',
                              keyfile='private.key',
                              server_side=True),
            application)

其中certfile和keyfile是服务端的证书和私钥文件,可用openssl命令生成自签名的证书和私钥。

虽然使用了一个第三方库,启动server的代码与wsgiref模块不太相同,但是之前编写的application回调函数可以直接在这里使用。也就是说我们的回调是与具体server无关的,更换另一个支持wsgi协议的server,只是在启动的代码做些修改,而我们所关心的业务逻辑则丝毫无需改动。

https://zhuanlan.zhihu.com/p/25939345



版权声明:本文为weixin_43571751原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。