博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
wsgi.py的详解
阅读量:2446 次
发布时间:2019-05-10

本文共 6897 字,大约阅读时间需要 22 分钟。

Python web开发中,服务端程序可分为2个部分:

  1. 服务器程序(用来接收、整理客户端发送的请求)
  2. 应用程序(处理服务器程序传递过来的请求)

    在开发应用程序的时候,我们会把常用的功能封装起来,成为各种框架,比如Flask,Django,Tornado(使用某框架进行web开发,相当于开发服务端的应用程序,处理后台逻辑)
    但是,服务器程序和应用程序互相配合才能给用户提供服务,而不同应用程序(不同框架)会有不同的函数、功能。 此时,我们就需要一个标准,让服务器程序和应用程序都支持这个标准,那么,二者就能很好的配合了

    WSGI:wsgi是python web开发的标准,类似于协议。它是服务器程序和应用程序的一个约定,规定了各自使用的接口和功能,以便二和互相配合

WSGI应用程序的部分规定

  1. 应用程序是一个可调用的对象
    可调用的对象有三种:
    1. 一个函数
    2. 一个类,必须实现__call__()方法
    3. 一个类的实例
  2. 这个对象接收两个参数

    从源码中,我们可以看到,这两个参数是environ, start_response. 以可调用对象为一个类为例:

    class application:    def __call__(self, environ, start_response):        pass
  3. 可调用对象需要返回一个可迭代的值。以可调用对象为一个类为例:

    class application:    def __call__(self, environ, start_response):        return [xxx]

WSGI服务器程序的部分规定

  1. 服务器程序需要调用应用程序

    def run(application):     #服务器程序调用应用程序    environ = {}     #设定参数    def start_response(xxx):     #设定参数        pass    result = application(environ, start_response)          #调用应用程序的__call__函数(这里应用程序是一个类)    def write(data):               pass    def data in result:          #迭代访问        write(data)
    服务器程序主要做了以下的事:     1. 设定应用程序所需要的参数     2. 调用应用程序     3. 迭代访问应用程序的返回结果,并传给客户端

Middleware

middleware是介于服务器程序和应用程序中间的部分,middleware对服务器程序和应用程序是透明的。

对于服务器程序来说,middleware就是应用程序,middleware需要伪装成应用程序,传递给服务器程序对于应用程序来说,middleware就是服务器程序,middleware需要伪装成服务器程序,接受并调用应用程序

服务器程序获取到了客户端请求的URL,需要把URL交给不同的函数处理,这个功能可以使用middleware实现:

# URL Routing middlewaredef urlrouting(url_app_mapping):        def midware_app(environ, start_response):       #函数可调用,包含2个参数,返回可迭代的值          url = environ['PATH_INFO']                 app = url_app_mapping[url]       #获得对应url的应用程序          result = app(environ, start_response)       #调用应用程序          return result            return midware_app

函数midware_app就是middleware:

一方面,midware_app函数设置了应用程序所需要的变量,并调用了应用程序。所以对于应用程序来说,它是一个服务器程序
另一方面,midware_app函数是一个可调用的对象,接收两个参数,同时可调用对象返回了一个可迭代的值。所以对于服务器程序来说,它是一个应用程序

写中间件(middleware)的逻辑:1. middleware需要伪装成应用程序—> WSGI应用程序的要求 —> 1. 可调用  2. 两个参数  3. 返回可迭代的值2. middleware需要伪装成服务器程序 —> WSGI服务器程序的要求 —> 调用应用程序

我们需要了解一下environ这个变量。在WSGI中, 应用程序需要两个参数:environstart_response, 在服务器程序调用应用程序之前, 需要先设定这两个参数。 其中start_response通常是个可调用的方法, 而environ则是一个字典, 它是在CGI中定义的, 查看CGI文档, 可以找到关于environ的定义。

以下是environ中的参数:

AUTH_TYPE  CONTENT_LENGTH        #HTTP请求中Content-Length的部分  CONTENT_TYPE          #HTTP请求中Content-Tpye的部分  GATEWAY_INTERFACE  HTTP_*                    #包含一系列变量, 如HTTP_HOST,HTTP_ACCEPT等  PATH_INFO             #URL路径除了起始部分后的剩余部分,用于找到相应的应用程序对象,如果请求的路径就是根路径,这个值为空字符串  PATH_TRANSLATED  QUERY_STRING          #URL路径中?后面的部分  REMOTE_ADDR  REMOTE_HOST  REMOTE_IDENT  REMOTE_USER  REQUEST_METHOD        #HTTP 请求方法,例如 "GET", "POST"  SCRIPT_NAME           #URL路径的起始部分对应的应用程序对象,如果应用程序对象对应服务器的根,那么这个值可以为空字符串  SERVER_NAME  SERVER_PORT  SERVER_PROTOCOL       #客户端请求的协议(HTTP/1.1 HTTP/1.0)  SERVER_SOFTWARE

举例:http://localhost:5000/aaa?666, 变量值为:

REQUEST_METHOD=‘GET’SCRIPT_NAME=''SERVER_NAME='localhost'SERVER_PORT=‘5000’PATH_INFO='/aaa'QUERY_STRING='666'SERVER_PROTOCOL='HTTP/1.1'CONTENT_TYPE='text/plain'CONTEN_LENGTH='' HTTP_HOST = 'localhost:8000'HTTP_ACCEPT = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'HTTP_ACCEPT_ENCODING = 'gzip,deflate,sdch'HTTP_ACCEPT_LANGUAGE = 'en-US,en;q=0.8,zh;q=0.6,zh-CN;q=0.4,zh-TW;q=0.2'HTTP_CONNECTION = 'keep-alive'HTTP_USER_AGENT = 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.77 Safari/537.36'

对于start_response()函数:

start_response是HTTP响应的开始, 它的形式为:start_response(status, response_headers, exc_info=None)
status表示HTTP状态码, 比如200 OK
response_headers是一个列表,列表元素是个tuple:(header_name, header_value)
exc_info是个可选参数, 当处理请求的过程中发生错误时, 会设置该参数, 同时会调用start_response
举一个werkzeug官方文档上的例子,我稍作改进,以进行分析(这段建议看完wsgi.py第二部分的SharedDataMiddleware类再看):

class Shortly(object):    def __init__(self, config):        self.redis = redis.Redis(config['redis_host'], config['redis_port'])    def dispatch_request(self, request):        return Response('Hello World!')     #初始化Response类    def wsgi_app(self, environ, start_response):        request = Request(environ)        response = self.dispatch_request(request)   #response的类型为Response类        print('%%%%%%%%%%%%%%%%%%%%%%%%%%%')        return response(environ, start_response)   #Response的__call__函数就是应用程序,返回迭代对象    def __call__(self, environ, start_response):        print(self.wsgi_app)        print('erghrgheoghegoierge')        return self. wsgi_app(environ, start_response)def create_app(redis_host='localhost', redis_port=6379, with_static=True):    app = Shortly({        'redis_host':       redis_host,        'redis_port':       redis_port    })    if with_static:        print('yes')        app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {            '/static':  os.path.join(os.path.dirname(__file__), 'static')        })    print('33333333333333333')    return app
#开启本地服务器if __name__ == '__main__':    from werkzeug.serving import run_simple    app = create_app()  #创建应用程序的实例    run_simple('127.0.0.1', 5000, app, use_debugger=True, use_reloader=True)

我们查看Response的源码(Response继承了BaseResponse,查看BaseResponse的源码即可)可以知道:函数dispatch_request()返回的值是Request的构造函数,即返回了一个Response类, 在函数wsgi_app()中,request的值的类型就是Response, 所以wsgi_app()的返回值response(environ, start_response)实际上是调用了Response类的__call__()函数。

看了源码我们可以发现,__call__()是一个WSGI的应用程序!

当运行这个程序的时候:

22222222222222222yes33333333333333333 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat22222222222222222yes33333333333333333

我们先不纠结为什么读取了2次。

当我们打开这个网页,控制台的输出为:

#说明wsgi_app是SharedDataMiddleware的实例! erghrgheoghegoierge%%%%%%%%%%%%%%%%%%%%%%%%%%% #说明执行了原wsgi_app函数中的内容!127.0.0.1 - - [22/May/2015 21:01:25] "GET / HTTP/1.1" 200 -

可以注意到,在本例中,app.wsgi_app这个方法已经变成了一个SharedDataMiddleware类的实例,我很好奇当服务器把environ和start_response传递给app后,为什么wsgi_pp还会执行原wsgi_app中的内容呢?

当我们访问主机地址的时候,服务器程序接受用户请求,然后会把environ和start_response传递给应用程序app,app会执行__call__函数,在该函数中,会执行app.wsgi_app这个函数。 然后wsgi_app会执行ShareDataMiddleware__call__()函数
这地方需要我们看SharedDataMiddleware类的__call__()的源码。看了源码我们可以发现,由于用户没有请求静态文件,所以会执行return self.app(environ, start_response),在本例中,我们可以看到在create_app()中,我们定义的ShareDataMiddleware的应用程序是app.wsgi_app,所以这里返回的是原wsgi_app函数!所以当然会执行原函数了~
同时,我们也可以在static文件夹中放一个文件,然后访问试一下:127.0.0.1/static/文件名,此时就能看到那个文件了!
通过本例,我们可以更深刻的了解Middleware的作用:

Middleware介于服务器程序和应用程序之间,它会接收服务器发来的消息(environ和start_response),并做一定的处理,然后把需要应用程序处理的部分传递给应用程序处理

另外, 服务器程序还需要定义WSGI的相关变量:

wsgi.version                                                  值的形式为 (1, 0) 表示 WSGI 版本 1.0                             wsgi.url_scheme                                               表示 url 的模式,例如 "https" 还是 "http"                        wsgi.input                                                    输入流,HTTP请求的 body 部分可以从这里读取                         wsgi.erros                                                    输出流,如果出现错误,可以写往这里wsgi.multithread如果应用程序对象可以被同一进程中的另一线程同时调用,这个值为Truewsgi.multiprocess如果应用程序对象可以同时被另一个进程调用,这个值为Truewsgi.run_once如果服务器希望应用程序对象在包含它的进程中只被调用一次,那么这个值为True

转载地址:http://rqtqb.baihongyu.com/

你可能感兴趣的文章
fitbit手表中文说明书_Fitbit OS达到3.0版,这是新功能
查看>>
ublock origin_Chrome可能会在打破uBlock起源的同时更快地阻止广告
查看>>
电邮地址_我如何找出电子邮件的真正来源?
查看>>
windows虚拟桌面_在Windows中使用虚拟桌面的最佳免费程序
查看>>
ipad iphone开发_如何在iPhone或iPad上的消息中快速选择表情符号
查看>>
在windows使用gpu_如何选择Windows 10上游戏使用的GPU
查看>>
minecraft启动器_如何使用外部编辑器编辑Minecraft地图
查看>>
什么是适用于iPhone和iPad的iOS最新版本?
查看>>
成为产品不一定是坏事
查看>>
Ubuntu 18.04 LTS现在在Microsoft Store中
查看>>
如何检查已安装的Ubuntu版本
查看>>
如何在Windows 10上禁用附近共享
查看>>
gmail_Gmail将提供自毁电子邮件
查看>>
google 禁止广告_是否应禁止针对个人的广告?
查看>>
Plover.io在本地设备之间快速共享文件
查看>>
如何在OS X照片中禁用iCloud照片同步
查看>>
Minecraft的官方网站分发了受恶意软件感染的皮肤
查看>>
word模板快速填内容_如何快速轻松地在Word中选择内容块
查看>>
如何在Word 2013中直接从一个表导航到另一个表
查看>>
twitch 录像_如何通过NVIDIA GeForce Experience将您的PC游戏玩法传送到Twitch
查看>>