原标题:Python入门基础教程:WSGI

WSGI 简介

WSGI 是什么

WSGI 是 Python Web Server Gateway Interface 的缩写,是描述 Web 服务器与 Python 应用程序之间如何交互的接口规范。该规范具体描述在 PEP-3333。

这个规范相当于 Web 服务器和 Python 应用程序之间的桥梁。对于 Web 服务器,WSGI 描述了如何把用户的请求数据交给 Python 应用程序;对于 Python 应用程序,WSGI 描述了如何获得用户的请求数据,如何将请求的处理结果返回给 Web 服务器。

WSGI 应用程序

符合 WSGI 规范的 Python 应用程序必须是:

一个可调用对象(callable object,例如函数、类、实现了 __call__ 方法的实例)

接受两个由 WSGI Server 提供的参数:

environ 字典,包含 环境变量,请求数据,如 REQUEST_METHOD,PATH_INFO,QUERY_STRING 等

start_response 函数,开始响应请求的回调函数,用于发送响应状态(HTTP status) 和响应头(HTTP headers)

返回一个由 bytes 类型元素组成的可迭代对象(通常是一个字节序列),即响应正文(Response body)

下面分别以函数、类、实现了 __call__ 方法的实例来演示符合 WSGI 规范的 Python 应用程序:

# 可调用对象是一个函数

def simple_app(environ, start_response):

# 响应状态(状态码和状态信息)

status = '200 OK'

# 响应体

response_body = b"Hello WSGI"

# 响应头,是一个列表,每对键值都必须是一个 tuple

response_headers = [('Content-type', 'text/plain'),

('Content-Length', str(len(response_body)))]

# 回调 WSGI 服务器提供的 start_response,返回响应状态和响应头

start_response(status, response_headers)

# 返回响应体,由 bytes 类型元素组成的可迭代对象

return [response_body]

# 可调用对象是一个类

class AppClass:

"""可调用对象是 AppClass 类,调用方法:

for body in AppClass(env, start_response):

process_body(body)

"""

def __init__(self, environ, start_response):

self.environ = environ

self.start = start_response

def __iter__(self):

status = '200 OK'

response_body = b"Hello WSGI"

response_headers = [('Content-type', 'text/plain'),

('Content-Length', str(len(response_body)))]

self.start(status, response_headers)

yield response_body

# 可调用对象是一个类实例

class AnotherAppClass:

"""可调用对象是 AnotherAppClass 类实例,调用方法:

app = AnotherAppClass()

for body in app(env, start_response):

process_body(body)

"""

def __init__(self):

pass

def __call__(self, environ, start_response):

status = '200 OK'

response_body = b"Hello WSGI"

response_headers = [('Content-type', 'text/plain'),

('Content-Length', str(len(response_body)))]

start_response(status, response_headers)

yield response_body

WSGI 服务器

跟 WSGI 应用程序对应的 WSGI 服务器需要完成以下工作:

接收 HTTP 请求,返回 HTTP 响应

提供 environ 数据,实现回调函数 start_response

调用 WSGI application,并将 environ,start_response 作为参数传入

简化版 WSGI 服务器内部的实现流程:

import os, sys

def unicode_to_wsgi(u):

return u.decode('utf-8')

def wsgi_to_bytes(s):

return s.encode('utf-8')

# application 是 WSGI 应用程序,一个可调用对象

def run_with_cgi(application):

# 准备 environ 参数数据

# 内部包含本次 HTTP 请求的数据,如 REQUEST_METHOD, PATH_INFO, QUERY_STRING 等

environ = {k: unicode_to_wsgi(v) for k,v in os.environ.items()}

# WSGI 环境变量

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 Asserti("write() before start_response()")

elif not headers_sent:

# 在第一次发送响应体之前,发送已经存在的响应头

status, response_headers = headers_sent[:] = headers_set

out.write(wsgi_to_bytes('Status: %s\r\n' % status))

for header in response_headers:

out.write(wsgi_to_bytes('%s: %s\r\n' % header))

out.write(wsgi_to_bytes('\r\n'))

out.write(data)

out.flush()

# start_response 回调函数,根据 WSGI 应用程序传递过来的 HTTP status 和 response_headers

# 设置响应状态和响应头

def start_response(status, response_headers, exc_info=None):

# 处理异常情况

if exc_info:

pass

headers_set[:] = [status, response_headers]

return write

# 调用 WSGI 应用程序,传入准备好的 environ(请求数据)和 start_response(开始响应回调函数)

result = application(environ, start_response)

# 处理响应体

try:

for data in result:

if data:

write(data)

finally:

if hasattr(result, 'close'):

result.close()

Middleware

Middleware(中间件) 处于 WSGI 服务器和 WSGI 应用程序之间。对于 WSGI 应用程序它相当于 WSGI 服务器,而对于 WSGI 服务器 它相当于 WSGI 应用程序。它很像 WSGI 应用程序,接收到请求之后,做一些针对请求的处理,同时它又能在接收到响应之后,做一些针对响应的处理。所以 Middleware 的特点是:

被 WSGI 服务器或其他 Middleware 调用,返回 WSGI 应用程序

调用 WSGI 应用程序,传入 environ 和 start_response

我们以白名单过滤和响应后续处理来演示 Middleware:

from wsgiref.simple_server import make_server

def app(environ, start_response):

# 响应状态(状态码和状态信息)

status = '200 OK'

# 响应体

response_body = b"Hello WSGI"

# 响应头,是一个列表,每对键值都必须是一个 tuple

response_headers = [('Content-type', 'text/plain'),

('Content-Length', str(len(response_body)))]

# 回调 WSGI 服务器提供的 start_response,返回响应状态和响应头

start_response(status, response_headers)

# 返回响应体,由 bytes 类型元素组成的可迭代对象

return [response_body]

# 针对请求数据进行处理的中间件

class WhitelistMiddleware(object):

def __init__(self, app):

self.app = app

# 类实例被调用时,根据从请求中获得的 HTTP_HOST 实现白名单功能

def __call__(self, environ, start_response):

ip_addr = environ.get('HTTP_HOST').split(':')[0]

if ip_addr not in ('127.0.0.1'):

start_response('403 Forbidden', [('Content-Type', 'text/plain')])

return [b'Forbidden']

return self.app(environ, start_response)

# 针对响应数据进行处理的中间件

class UpperMiddleware(object):

def __init__(self, app):

self.app = app

# 类实例被调用时,将响应体的内容转换成大写格式

def __call__(self, environ, start_response):

for data in self.app(environ, start_response):

yield data.upper()

if __name__ == '__main__':

app = UpperMiddleware(WhitelistMiddleware(app))

with make_server('', 8000, app) as httpd:

print("Serving on port 8000...")

httpd.serve_forever()

上面例子是一份完整可运行的代码。函数 app 是 WSGI 应用程序,WhitelistMiddleware 和 UpperMiddleware 是 WSGI Middleware,WSGI 服务器使用的是 Python 内置的 wsgiref 模块(wsgiref 模块是 Python 3 提供的 WSGI 规范的参考实现,wsgiref 中的 WSGI 服务器可用于开发测试,不能使用在生产环境)。

在 WSGI 规范中给出了一些 Middleware 的使用场景,其中根据请求路径分发到不同应用程序的场景,正是一个 Web Framework 最基本的一项功能。下面我们来看一个通过 Middleware 实现的路由转发例子:

from wsgiref.simple_server import make_server

# 请求 path 分发中间件

class RouterMiddleware(object):

def __init__(self):

# 保存 path 与应用程序对应关系的字典

self.path_info = {}

def route(self, environ, start_response):

application = self.path_info[environ['PATH_INFO']]

return application(environ, start_response)

# 类实例被调用时,保存 path 和应用程序对应关系

def __call__(self, path):

def wrapper(application):

self.path_info[path] = application

return wrapper

router = RouterMiddleware()

@router('/hello') # 调用 RouterMiddleware 类实例,保存 path 和应用程序对应关系

def hello(environ, start_response):

status = '200 OK'

response_body = b"Hello"

response_headers = [('Content-type', 'text/plain'),

('Content-Length', str(len(response_body)))]

start_response(status, response_headers)

return [response_body]

@router('/world')

def world(environ, start_response):

status = '200 OK'

response_body = b'World'

response_headers = [('Content-type', 'text/plain'),

('Content-Length', str(len(response_body)))]

start_response(status, response_headers)

return [response_body]

@router('/')

def hello_world(environ, start_response):

status = '200 OK'

response_body = b'Hello World'

response_headers = [('Content-type', 'text/plain'),

('Content-Length', str(len(response_body)))]

start_response(status, response_headers)

return [response_body]

def app(environ, start_response):

return router.route(environ, start_response)

if __name__ == '__main__':

with make_server('', 8000, app) as httpd:

print("Serving on port 8000...")

httpd.serve_forever()

WSGI 接口规范描述的 WSGI 应用程序太过于底层,对于开发人员很不友好。人们通常会使用 Web Framework 来完成一个 Web 应用的开发工作,然后会把这个 Web 应用部署在为生产环境准备的 Web 服务器上。

常用的 Python Web Framework:

Django

一个功能完备的 Web 框架,拥有庞大的开发者社区和丰富的第三方库。

Flask

一款微型框架,构建更小应用、API 和 web 服务。是任何不适用 Django 的 Python web 应用的默认选择。

Tornado

一个异步 web 框架,原生支持 WebSocket。

Bottle

更小的 Web 框架,整个框架只有一个 Python 文件,是不错的源码学习案例。

常用的 WSGI Web Server:

Gunicorn

纯 Python 实现的 WSGI 服务器,拥有十分简单的配置和十分合理的默认配置,使用简单。

uWSGI

基于 uwsgi 协议的,功能十分强大的 Web 服务器,同时也支持 Python WSGI 协议。性能很好,但配置复杂。

责任编辑:

python基础教程运行程序_Python入门基础教程:WSGI相关推荐

  1. python监控linux运行程序_python linux监控程序

    Pyinotify – Linux中实时监控文件系统更改 Pyinotify 是一个简单而实用的 Python 模块,它用于通过 inotify 实时监控Linux文件系统的更改.用于在Linux中实 ...

  2. python创建对象的格式为_Python入门基础学习(面向对象)

    python基础学习笔记(四) 面向对象的三个基本特征: 封装:把客观事物抽象并封装成对象,即将属性,方法和事件等集合在一个整体内 继承:允许使用现有类的功能并在无须重新改写原来的类情况下,对这些功能 ...

  3. python列表元素为中文_python入门基础教程之Python list列表修改元素

    python提供了两种修改列表(list)元素的方法,你可以每次修改单个元素,也可以每次修改一组元素(多个). 修改单个元素 修改单个元素非常简单,直接对元素赋值即可.请看下面的例子: nums = ...

  4. python unicode编码转换中文_Python入门高级教程--Python 中文编码

    Python 中文编码 前面章节中我们已经学会了如何用 Python 输出 "Hello, World!",英文没有问题,但是如果你输出中文字符 "你好,世界" ...

  5. python代码变成运行程序_python脚本转化单个exe执行程序

    操作系统平台: Windows Server 2003 R2 Enterprise SP2 X86简体中文版 1.ActivePython-2.7.5.6-win32-x86.msi 2.setupt ...

  6. python绿色版运行程序_Python打包exe运行程序,分享你的技术成果!

    本文转载于公众号:你想要

  7. 匹配正则_程序员入门基础:python正则表达式贪婪匹配和非贪婪匹配

    此文为python正则表达式的高阶入门,正则基础入门请参考程序员入门基础:python的正则表达式. 一.贪婪匹配和非贪婪匹配 举例说明概念: print('非贪婪匹配',re.search('el+ ...

  8. 好程序员web前端教程分享web前端入门基础知识

    好程序员web前端教程分享web前端入门基础知识,作为合格的Web前端工程师必须得掌握HTML.CSS和JavaScript.只懂其中一两个还不行,必须对这三门语言都要熟悉.下面我们一起来看一看吧! ...

  9. 视频教程-微信小程序从入门基础(第一季)-PHP

    微信小程序从入门基础(第一季) 多年一线互联网开发实战以及培训经验,对php开发,linux运维架构有丰富的经验,善于分析问题,解决问题. lampol ¥117.00 立即订阅 扫码下载「CSDN程 ...

最新文章

  1. gamma函数及相关其分布
  2. run-time cloud server system development recode
  3. LeetCode5-最长回文子串原理及Python实现
  4. jquery pager 访问 java_基于JQuery的Pager分页器实现代码
  5. Salesforce LWC学习(二) helloWorld程序在VSCode中的实现
  6. go url 参数编码和解码
  7. 详解animate.css动画插件用法
  8. 用C 程序理解汉字的机内码表示
  9. 网卡不兼容linux系统,CentOS与Broadcom 5709兼容性问题导致业务网络中断
  10. 计算机资源管理器总是未响应,资源管理器总是无响应,而且开机很慢老是解决不了问题...
  11. 【顺序、分支、循环、子程序设计】—— 微机原理实验
  12. 一大波无门槛优惠券来袭(仅限300张)
  13. 10分钟搭建一个免费个人博客网站
  14. 表格提示html内容消失,如何解决Word里面的表格插入题注后页面上内容消失、无法编辑的问题...
  15. Linux操作系统——切换到root用户及其他用户
  16. Anaconda的升级与卸载
  17. win10下禁止自动更新,Window Update禁用无效后续方法
  18. ORACLE exp时出现1455错误,全网唯一正解,建议收藏
  19. 印度进口战斗机想退货,因系统无法识别自家口音
  20. 开学季都有哪些数码产品推荐?2022年数码好物推荐

热门文章

  1. IAAS云计算产品畅想-云主机产品内涵
  2. Caddy、 SSLDocker、Nginx 性能比较及使用体验
  3. Spring Cloud CLI简介
  4. MAC下 Intellij IDEA GO语言插件安装及简单案例
  5. 《算法》笔记 17 - 数据压缩
  6. CentOS7升级内核kernel5.0
  7. excel如何快速选中某个区域
  8. 部署flas到服务器:No module named flask
  9. SpringMVC 异步交互 AJAX 文件上传
  10. java 课后习题 删除奇数元素下标 然后再删除值为奇数的下标