WSGI(Web Server Gateway Interface)是一个规范,用于描述在 Python 中 Web 服务器和 Web 应用程序之间如何交互,以及如何串接多个 Web 应用程序来处理 Web 请求。WSGI 规范通过 Python PEP3333 标准详细描述。
介绍 WSGI 既不是 Web 服务器,也不是 Web 框架,它只是一个接口规范 ,用于描述在 python 中 Web 服务器和 Web 应用程序之间如何交互。如果 Web 应用程序遵循 WSGI 规范,那么这个 Web 应用程序可以在任何符合 WSGI 规范的服务器上运行。多个兼容 WSGI 的 Web 应用程序也可以形成 stack,此时 stack 中间的 Web 应用程序也被称为 middleware,middleware 必须同时实现 WSGI server 和 application 侧的接口。
WSGI server(兼容 WSGI 规范的 Web server)从客户端接收请求,将其传递给应用程序,并将应用程序返回的响应发送给客户端。
应用程序接口 WSGI 应用程序接口
由一个可调用对象实现:函数、方法、类 或者实现了 object.__call__()
方法的实例。该可调用对象需要满足以下条件:
接收两个位置参数:
参数 1:一个字典,包含一系列环境变量(类似于 CGI 环境变量),这些变量由 server 根据所处理的每个请求而产生
参数 2:一个回调函数,由 server 提供,由应用程序使用。应用程序使用该回调函数向 Web server 返回 HTTP 响应状态码以及 HTTP 响应头部
在一个可迭代对象中封装返回给 server 的响应 body 字符串(字节字符串)
示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from wsgiref.simple_server import make_serverdef application (environ, start_response ): response_body = '\n' .join(('{}: {}' .format (k, v) for k, v in environ.items())) status = '200 OK' response_headers = [ ('Content-Type' , 'text/plain' ), ('Content-Length' , str (len (response_body))) ] start_response(status, response_headers) return [response_body.encode()] httpd = make_server('localhost' , 8051 , application) httpd.handle_request()
在该示例中,编写了一个 WSGI 应用程序 application
,该 web 应用程序总是返回 200
,并将 environ 字典的内容作为响应 body 返回
使用 Python 标准库中 wsgiref 模块提供的 WSGI server 作为 Web server,并将该应用程序提供给 WSGI server
实际运行结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 SHELL: /bin/bash KERNEL_FLAVOR: linux no_proxy: localhost,127.0.0.1,git.in.chaitin.net,portus.in.chaitin.net,s3-ephemeral.in.chaitin.net,registry-mirrors.dev.in.chaitin.net,mirror.dev.in.chaitin.net,172.17.0.2,10.2.28.5 TERM_PROGRAM_VERSION: 3.2a TMUX: /tmp/tmux-0/default,2722,1 TMUX_PLUGIN_MANAGER_PATH: /root/.tmux/plugins/ EDITOR: /usr/bin/vim PWD: /root/code/private/python/wsgi LOGNAME: root XDG_SESSION_TYPE: tty MOTD_SHOWN: pam HOME: /root LANG: en_US.UTF-8 LS_COLORS: rs=0:di=01;34:ln =01;36:mh=00:pi=40;33:so=01;35:do =01;35:bd=40;33;01:cd =40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm =01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36: LC_TERMINAL: iTerm2 https_proxy: http://proxy.in.chaitin.net:8123 SSH_CONNECTION: 10.2.7.21 52704 10.9.33.133 22 LESSCLOSE: /usr/bin/lesspipe %s %s XDG_SESSION_CLASS: user TERM: tmux-256color LESSOPEN: | /usr/bin/lesspipe %s USER: root TMUX_PANE: %11 LC_TERMINAL_VERSION: 3.4.16 SHLVL: 2 XDG_SESSION_ID: 1 http_proxy: http://proxy.in.chaitin.net:8123 XDG_RUNTIME_DIR: /run/user/0 SSH_CLIENT: 10.2.7.21 52097 22 DEBUGINFOD_URLS: XDG_DATA_DIRS: /usr/local/share:/usr/share:/var/lib/snapd/desktop PATH: /root/bin/:/root/.cargo/bin:/root/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin:/usr/local/go/bin DBUS_SESSION_BUS_ADDRESS: unix:path=/run/user/0/bus SSH_TTY: /dev/pts/0 OLDPWD: /root/docker_share/code/safeline-2/package TERM_PROGRAM: tmux _: /usr/bin/python3 SERVER_NAME: localhost GATEWAY_INTERFACE: CGI/1.1 SERVER_PORT: 8051 REMOTE_HOST: CONTENT_LENGTH: SCRIPT_NAME: SERVER_PROTOCOL: HTTP/1.1 SERVER_SOFTWARE: WSGIServer/0.2 REQUEST_METHOD: GET PATH_INFO: / QUERY_STRING: REMOTE_ADDR: 127.0.0.1 CONTENT_TYPE: text/plain HTTP_HOST: 127.0.0.1:8051 HTTP_USER_AGENT: curl/7.81.0 HTTP_ACCEPT: */* wsgi.input: <_io.BufferedReader name=4> wsgi.errors: <_io.TextIOWrapper name='<stderr>' mode='w' encoding='utf-8' > wsgi.version: (1, 0) wsgi.run_once: False wsgi.url_scheme: http wsgi.multithread: False wsgi.multiprocess: False wsgi.file_wrapper: <class 'wsgiref.util.FileWrapper' >
下面的程序添加了一个 middleware,它的作用是在 applicaiton 的响应 body 之前添加 Hello, WSGI
字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from wsgiref.simple_server import make_serverdef application (environ, start_response ): response_body = '\n' .join(('{}: {}' .format (k, v) for k, v in environ.items())) status = '200 OK' response_headers = [ ('Content-Type' , 'text/plain' ), ('Content-Length' , str (len (response_body))) ] start_response(status, response_headers) return [response_body.encode()] class HelloMidlleware : def __init__ (self, app ): self._app = app def __call__ (self, environ, start_response ): results = self._app(environ, start_response) yield b"Hello, WSGI\n" yield from results httpd = make_server('localhost' , 8051 , HelloMidlleware(application)) httpd.handle_request()
运行结果如下:
1 2 3 4 5 Hello, WSGI SHELL: /bin /bash KERNEL_FLAVOR: linux ......
Reference wsgi WSGI tutorial