文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

WSGI -Web 服务器与 Python 应用程序如何交互规范

2023-06-02 07:03

关注

今天就跟大家聊聊有关WSGI -Web 服务器与 Python 应用程序如何交互规范,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

WSGI -Web 服务器与 Python 应用程序如何交互规范

WSGI 是什么

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

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

WSGI -Web 服务器与 Python 应用程序如何交互规范

WSGI 应用程序

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

下面分别以函数、类、实现了 __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 服务器需要完成以下工作:

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

import os, sysdef 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 AssertionError("write() before start_response()") elif not headers_sent: # 在第一次发送响应体之前,发送已经存在的响应头 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() # 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 的特点是:

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

from wsgiref.simple_server import make_serverdef 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 wrapperrouter = 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:

常用的 WSGI Web Server:

看完上述内容,你们对WSGI -Web 服务器与 Python 应用程序如何交互规范有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注编程网行业资讯频道,感谢大家的支持。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-服务器
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯