本文参考了:

  • github.com/alanctkc/ws…
  • Youtube : Creating WSGI Middleware

上篇文章简要提到:wsgi 规范中的 app 是一个可调用对象,可以通过嵌套调用的方式实现中间件的功能。这篇文章就来亲自动手实现一下。

此文的重点在于 app 端,所以 wsgi 服务器将使用python 内置module wsgiref.simple_server 中的make_server

创建 app

新建文件 app.py :

def application(environ, start_response):"""The web application."""response_body = ""for key, value in environ.items():response_body += "<p>{} : {}\n</p>".format(key, value)# Set up the response status and headersstatus = '200 OK'response_headers = [('Content-Type', 'text/html; charset=utf-8'),('Content-Length', str(len(response_body))),]start_response(status, response_headers)return [response_body.encode('utf-8')]复制代码

注意:python3中要求 response_body是 bytes,所以需要 encode()一下。在 python2中是 str,不需要 encode()。

这个 app 做的事情非常简单,把传过来的 environ 原样返回。在开始返回body 之前,调用server传过来的start_response函数。

简要说明一下为什么是 retuen [response_body]而不是 return response_body或者 return response_body.split("\n")或者return response_body.split("")?

  • 首先 wsgi 规范说明了app返回的是一个可迭代对象,列表是可迭代的。
  • 其次,对于大多数 app 来说,response_body都不会太长,服务器的内存完成足以一次性装下,所以最高效的方法就是一次性把response_body全传过去。

创建 server

新建文件server.py

from wsgiref.simple_server import make_server
from app import applicationprint("Server is running at http://localhost:8888 . Press Ctrl+C to stop.")
server = make_server('localhost', 8888, application)
server.serve_forever()复制代码

用浏览器打开 http://localhost:8888,就可以看到 environ 的详细内容。其中比较重要的我用红框框圈了起来。

第一个中间件:cors

简要了解一下 cors 的机制(详细的要比这个复杂点):

如果一个ajax请求(XMLHttpRequest)是跨域的,比如说在 http://localhost:9000页面上向运行在http://localhost:8888的服务器发起请求,浏览器就会往请求头上面加上一个ORIGIN字段,这个字段的值就是localhost:9000。(对应在app 的 environ 参数中,就是 HTTP_ORIGIN

同时,浏览器会先发出OPTIONS请求,服务器要实现这样的功能:如果想要接收这个请求的话,需要在response 的 headers里面添加一个Access-Control-Allow-Origin字段,值就是请求传过来的那个ORIGIN

浏览器发出OPTIONS请求并发现返回数据的 headers 里面有Access-Control-Allow-Origin,才会进行下一步发出真正的请求:GET,POST,WAHTERVER。

所以,CORS 是浏览器和 Server共同协作来完成的。

看一下代码:

class CORSMiddleware(object):def __init__(self, app, whitelist=None):"""Initialize the middleware for the specified app."""if whitelist is None:whitelist = []self.app = appself.whitelist = whitelistdef validate_origin(self, origin):"""Validate that the origin of the request is whitelisted."""return origin and origin in self.whitelistdef cors_response_factory(self, origin, start_response):"""Create a start_response method that includes a CORS header for thespecified origin."""def cors_allowed_response(status, response_headers, exc_info=None):"""This wraps the start_response behavior to add some headers."""response_headers.extend([('Access-Control-Allow-Origin', origin)])return start_response(status, response_headers, exc_info)return cors_allowed_responsedef cors_options_app(self, origin, environ, start_response):"""A small wsgi app that responds to preflight requests for thespecified origin."""response_body = 'ok'status = '200 OK'response_headers = [('Content-Type', 'text/plain'),('Content-Length', str(len(response_body))),('Access-Control-Allow-Origin', origin),('Access-Control-Allow-Headers', 'Content-Type'),]start_response(status, response_headers)return [response_body.encode('utf-8')]def cors_reject_app(self, origin, environ, start_response):response_body = 'rejected'status = '200 OK'response_headers = [('Content-Type', 'text/plain'),('Content-Length', str(len(response_body))),]start_response(status, response_headers)return [response_body.encode('utf-8')]def __call__(self, environ, start_response):"""Handle an individual request."""origin = environ.get('HTTP_ORIGIN')if origin:if self.validate_origin(origin):method = environ.get('REQUEST_METHOD')if method == 'OPTIONS':return self.cors_options_app(origin, environ, start_response)return self.app(environ, self.cors_response_factory(origin, start_response))else:return self.cors_reject_app(origin, environ, start_response)else:return self.app(environ, start_response)复制代码

__init__方法传入的参数有:下一层的 app(回顾一下前面说的 app 是一层一层的,所以能够实现中间件)和 client 白名单,只允许来自这个白名单内的ajax 请求。

__call__方法说明这是一个可调用对象(类也可以是可调用的),一样接收两个参数:environstart_response。首先判断一下 environ 中有没有HTTP_ORIGIN,有的话就表明属于跨域请求。如果是跨域,判断一下 origin 在不咋白名单。如果在白名单里面,如果是 OPTIONS请求,返回cors_options_app里面的对应内容(加上了Access-Control-Allow-Origin header);如果不是OPTIONS请求,调用下一层的 app。如果不在白名单,返回的是cors_reject_app

修改一下server.py:

app = CORSMiddleware(app=application,whitelist=['http://localhost:9000','http://localhost:9001']
)
server = make_server('localhost', 8000, app)
复制代码

测试 cors app

这里在运行三个客户端,[代码在此]。(github.com/liaochangji…)

运行python client.py

在浏览器打开http://localhost:9000http://localhost:9001http://localhost:9002,可以发现http://localhost:9000http://localhost:9001成功发出了请求,而http://localhost:9002失败了。

第二个中间件:请求耗时

这个比上一个要简单很多,相信现在你已经完全能够理解了:

import timeclass ResponseTimingMiddleware(object):"""A wrapper around an app to print out the response time for eachrequest."""def __init__(self, app):self.app = appdef __call__(self, environ, start_response):"""Meaure the time spent in the application."""start_time = time.time()response = self.app(environ, start_response)response_time = (time.time() - start_time) * 1000timing_text = "总共耗时: {:.10f}ms \n".format(response_time)response = [timing_text.encode('utf-8') + response[0]]return response
复制代码

再修改一下server.py

app = ResponseTimingMiddleware(CORSMiddleware(app=application,whitelist=['http://localhost:9000','http://localhost:9001'])
)
复制代码

再次访问http://localhost:8000,会看到最前面打印出了此次请求的耗时:

总结一下

我手画了一个请求图,希望对你有所帮助:

本文的所有源代码开源在 github 上:github.com/liaochangji…

希望能点个 star ~

如果你像我一样真正热爱计算机科学,喜欢研究底层逻辑,欢迎关注我的微信公众号:

Python Web开发:开发wsgi中间件相关推荐

  1. 仅需8道题轻松掌握Python Web应用开发 | Python技能树征题

    仅需9道题轻松掌握Python Web应用开发 | Python技能树征题 0. 前言 1. 第 1 题:客户端访问 Web 服务器 2. 第 2 题:构建 TCP 服务器 3. 第 3 题:构建 U ...

  2. 用python做网站开发的课程_腾讯课堂:Flask Python Web 网站开发

    大家好,欢迎大家学习优品课堂出品的Python完全零基础入我们精讲的系列教程这节课 我们来看第一个,我们先了解一下计算机常识,这节课我们来介绍.计算机的概念和组成,那不仅是开发人员作为普通用户电脑的使 ...

  3. 失业在家抠脚的我花了2个月,读完了这份《Python Web接口开发与测试》,我居然进华为了...

    学习计划 失业在家抠脚到华为年薪25w测试工程师,我只花了2个月~ 底层逻辑 如果要进大厂,算法.底层.项目经验都要刷,小编以后会给大家更新各种面试题-- 如果要进大厂,项目经验.底层算法.网络.数据 ...

  4. Python Web技术开发软件安装

    关于博主 努力与运动兼备-~~有任何问题可以加我好友或者关注微信公众号,欢迎交流,我们一起进步! 微信公众号: 啃饼思录 QQ: 2810706745(i思录) 今天,博主正好有空,看到网上那么多求助 ...

  5. Python Web开发之WSGI

    Python Web开发之WSGI WSGI(全称Web Server Gate Interface,Web服务器网关接口)是Python为了规范和简化Web服务开发过程,定义了一种Web服务器和应用 ...

  6. python 免费空间_免费云空间/VPS AppFog申请及Python Web应用开发与上传实战

    欢迎转载,但请勿用于商业用途. 联系方式:jmppok@gmail.com 目前网络上有很多提供云存储.云空间.VPS的厂商,从功能.稳定性.易用性.人气等方面考虑,作者认为AppFog是一个不错的选 ...

  7. python——Web服务开发(一)Flask模块

    flask的诞生于2010年的愚人节,本来它只是作者无意间写的一个小玩具,没想到它却悄悄流行起来了.漫长的8年时间,flask一直没有发布一个严肃的正式版本,但是却不能阻挡它成为即将被微软收购的亚洲最 ...

  8. python开发工程师面试题-一名python web后端开发工程师的面试总结

    背景介绍 工作一年多不到两年.之前一直在做C++的MFC软件界面开发工作.公司为某不景气的国企研究所.(喏,我的工作经验很水:1是方向不对:2是行业有偏差).然后目前是在寻找python后端开发这一块 ...

  9. python web前端开发面试_面试前端,听听别人怎么说!

    分享一个人的面试经验: 一年半经验,百度.有赞.阿里面试总结 前言 人家都说,前端需要每年定期出来面面试,衡量一下自己当前的技术水平以及价值,本人17年7月份,毕业到现在都没出来试过,也没很想换工作, ...

  10. python——Web服务开发(二)分布式缓存

    上一篇博客写了flask模块实现web服务搭建的基本方法以及简单的缓存功能,但是这种缓存随着服务重启便会丢失,也无法满足多个服务共享缓存的需求,因此,我们通过redis来实现web服务的分布式缓存. ...

最新文章

  1. 智源论坛 | 智能处理器探索(3月21日)
  2. 【Linux】14.ubuntu忘记root密码、用户密码输入次数过多锁住的问题
  3. C++基本数据类型解惑
  4. ThreadLocal就是这么简单
  5. 地理素养的核心构成和主要特点
  6. 6818 开发板 配置 ubuntu 桌面环境 与 ROS
  7. MFC三大dll使用总结
  8. 关于AndroidStudio结合百度地图Api开发的SHA1获取
  9. ubuntu-linux下的精品软件大汇总
  10. python-snap7安装各种报错
  11. android自定义listview 显示数组,android TextView控件如何显示Listview数组内容到一个Textview控件上?...
  12. pathon和python_【pathon基础】初识python
  13. 路由器不显示连接该WiFi的设备
  14. java微信公众号开发及源码分享
  15. Windows Embedded CE和Windows Mobile下ActiveSync开发
  16. 局域网服务器怎么更改账号,怎么修改访问局域网共享用户名和密码
  17. Eclipse菜单project用法介绍
  18. 微软遭解雇的明星员工:后悔没早点离开微软
  19. 809计算机考研,中国农业大学2018年计算机考研809地理信息系统(含遥感原理)考试大纲...
  20. formality验证及svf文件含义(时序验证)

热门文章

  1. php列目录设置密码,PHP输入密码并列出目录文件生成超链接代码
  2. GPU Gems1 - 5 改良的Perlin噪声的实现
  3. iOS下载大文件原理解析一
  4. EM算法(Expectation Maximization Algorithm)
  5. luogu P1896 [SCOI2005]互不侵犯
  6. 51Nod 蜥蜴和地下室(搜索)
  7. float 常见用法与问题--摘抄
  8. union 和 union all 有什么不同?
  9. 跨线程取出控件的值的写法(不是跨线程赋予控件值)
  10. firedebug调试Jquery