详解Python程序与服务器连接的WSGI接口


Posted in Python onApril 29, 2015

了解了HTTP协议和HTML文档,我们其实就明白了一个Web应用的本质就是:

  •     浏览器发送一个HTTP请求;
  •     服务器收到请求,生成一个HTML文档;
  •     服务器把HTML文档作为HTTP响应的Body发送给浏览器;
  •     浏览器收到HTTP响应,从HTTP Body取出HTML文档并显示。

所以,最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。Apache、Nginx、Lighttpd等这些常见的静态服务器就是干这件事情的。

如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。

正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口,让我们专心用Python编写Web业务。

这个接口就是WSGI:Web Server Gateway Interface。

WSGI接口定义非常简单,它只要求Web开发者实现一个函数,就可以响应HTTP请求。我们来看一个最简单的Web版本的“Hello, web!”:

def application(environ, start_response):
  start_response('200 OK', [('Content-Type', 'text/html')])
  return '<h1>Hello, web!</h1>'

上面的application()函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:

  1.     environ:一个包含所有HTTP请求信息的dict对象;
  2.     start_response:一个发送HTTP响应的函数。

在application()函数中,调用:

start_response('200 OK', [('Content-Type', 'text/html')])

就发送了HTTP响应的Header,注意Header只能发送一次,也就是只能调用一次start_response()函数。start_response()函数接收两个参数,一个是HTTP响应码,一个是一组list表示的HTTP Header,每个Header用一个包含两个str的tuple表示。

通常情况下,都应该把Content-Type头发送给浏览器。其他很多常用的HTTP Header也应该发送。

然后,函数的返回值'<h1>Hello, web!</h1>'将作为HTTP响应的Body发送给浏览器。

有了WSGI,我们关心的就是如何从environ这个dict对象拿到HTTP请求信息,然后构造HTML,通过start_response()发送Header,最后返回Body。

整个application()函数本身没有涉及到任何解析HTTP的部分,也就是说,底层代码不需要我们自己编写,我们只负责在更高层次上考虑如何响应请求就可以了。

不过,等等,这个application()函数怎么调用?如果我们自己调用,两个参数environ和start_response我们没法提供,返回的str也没法发给浏览器。

所以application()函数必须由WSGI服务器来调用。有很多符合WSGI规范的服务器,我们可以挑选一个来用。但是现在,我们只想尽快测试一下我们编写的application()函数真的可以把HTML输出到浏览器,所以,要赶紧找一个最简单的WSGI服务器,把我们的Web应用程序跑起来。

好消息是Python内置了一个WSGI服务器,这个模块叫wsgiref,它是用纯Python编写的WSGI服务器的参考实现。所谓“参考实现”是指该实现完全符合WSGI标准,但是不考虑任何运行效率,仅供开发和测试使用。
运行WSGI服务

我们先编写hello.py,实现Web应用程序的WSGI处理函数:

# hello.py

def application(environ, start_response):
  start_response('200 OK', [('Content-Type', 'text/html')])
  return '<h1>Hello, web!</h1>'

然后,再编写一个server.py,负责启动WSGI服务器,加载application()函数:

# server.py
# 从wsgiref模块导入:
from wsgiref.simple_server import make_server
# 导入我们自己编写的application函数:
from hello import application

# 创建一个服务器,IP地址为空,端口是8000,处理函数是application:
httpd = make_server('', 8000, application)
print "Serving HTTP on port 8000..."
# 开始监听HTTP请求:
httpd.serve_forever()
Try

确保以上两个文件在同一个目录下,然后在命令行输入python server.py来启动WSGI服务器:

详解Python程序与服务器连接的WSGI接口

注意:如果8000端口已被其他程序占用,启动将失败,请修改成其他端口。

启动成功后,打开浏览器,输入http://localhost:8000/,就可以看到结果了:

详解Python程序与服务器连接的WSGI接口

在命令行可以看到wsgiref打印的log信息:

详解Python程序与服务器连接的WSGI接口

按Ctrl+C终止服务器。

如果你觉得这个Web应用太简单了,可以稍微改造一下,从environ里读取PATH_INFO,这样可以显示更加动态的内容:

# hello.py

def application(environ, start_response):
  start_response('200 OK', [('Content-Type', 'text/html')])
  return '<h1>Hello, %s!</h1>' % (environ['PATH_INFO'][1:] or 'web')

你可以在地址栏输入用户名作为URL的一部分,将返回Hello, xxx!:

详解Python程序与服务器连接的WSGI接口

是不是有点Web App的感觉了?
小结

无论多么复杂的Web应用程序,入口都是一个WSGI处理函数。HTTP请求的所有输入信息都可以通过environ获得,HTTP响应的输出都可以通过start_response()加上函数返回值作为Body。

复杂的Web应用程序,光靠一个WSGI函数来处理还是太底层了,我们需要在WSGI之上再抽象出Web框架,进一步简化Web开发。

Python 相关文章推荐
介绍Python的Django框架中的QuerySets
Apr 20 Python
全面解析Python的While循环语句的使用方法
Oct 13 Python
python http接口自动化脚本详解
Jan 02 Python
[原创]windows下Anaconda的安装与配置正解(Anaconda入门教程)
Apr 05 Python
python实现将读入的多维list转为一维list的方法
Jun 28 Python
Python 处理图片像素点的实例
Jan 08 Python
详解Python装饰器
Mar 25 Python
基于python实现自动化办公学习笔记(CSV、word、Excel、PPT)
Aug 06 Python
python tkinter图形界面代码统计工具
Sep 18 Python
基于Python下载网络图片方法汇总代码实例
Jun 24 Python
Python实现树莓派摄像头持续录像并传送到主机的步骤
Nov 30 Python
Python pandas之求和运算和非空值个数统计
Aug 07 Python
Python的SQLAlchemy框架使用入门
Apr 29 #Python
python使用post提交数据到远程url的方法
Apr 29 #Python
python实现根据ip地址反向查找主机名称的方法
Apr 29 #Python
连接Python程序与MySQL的教程
Apr 29 #Python
python实现通过代理服务器访问远程url的方法
Apr 29 #Python
python实现带错误处理功能的远程文件读取方法
Apr 29 #Python
python使用socket远程连接错误处理方法
Apr 29 #Python
You might like
ThinkPHP中使用ajax接收json数据的方法
2014/12/18 PHP
详解PHP中instanceof关键字及instanceof关键字有什么作用
2015/11/05 PHP
php实现给二维数组中所有一维数组添加值的方法
2017/02/04 PHP
使用laravel的migrate创建数据表的方法
2019/09/30 PHP
JavaScript脚本语言在网页中的简单应用
2007/05/13 Javascript
Jquey拖拽控件Draggable使用方法(asp.net环境)
2010/09/28 Javascript
JS获取页面窗口大小的代码解读
2011/12/01 Javascript
js模仿windows桌面图标排列算法具体实现(附图)
2013/06/16 Javascript
使用BootStrap和Metroui设计的metro风格微网站或手机app界面
2016/10/21 Javascript
jquery实现刷新随机变化样式特效(tag标签样式)
2017/02/03 Javascript
从零开始学习Node.js系列教程之基于connect和express框架的多页面实现数学运算示例
2017/04/13 Javascript
Node.js实现连接mysql数据库功能示例
2017/09/15 Javascript
layui 上传文件_批量导入数据UI的方法
2019/09/23 Javascript
js中Function引用类型常见有用的方法和属性详解
2019/12/11 Javascript
[14:20]刀塔大凶女神互压各路奇葩屌丝
2014/05/16 DOTA
Python实现爬取知乎神回复简单爬虫代码分享
2015/01/04 Python
python3序列化与反序列化用法实例
2015/05/26 Python
Python装饰器入门学习教程(九步学习)
2016/01/28 Python
python 函数传参之传值还是传引用的分析
2017/09/07 Python
解析Python中的eval()、exec()及其相关函数
2017/12/20 Python
Python编程求解二叉树中和为某一值的路径代码示例
2018/01/04 Python
将字典转换为DataFrame并进行频次统计的方法
2018/04/08 Python
python numpy存取文件的方式
2020/04/01 Python
python实现三种随机请求头方式
2021/01/05 Python
Html5 video标签视频的最佳实践
2020/02/26 HTML / CSS
法国二手手袋、手表和奢侈珠宝购物网站:Collector Square
2018/07/05 全球购物
八年级历史教学反思
2014/01/10 职场文书
我是一名护士演讲稿
2014/08/28 职场文书
地理科学专业自荐信
2014/09/01 职场文书
作风大整顿心得体会
2014/09/10 职场文书
大学生村官工作总结2015
2015/04/09 职场文书
老生常谈 使用 CSS 实现三角形的技巧(多种方法)
2021/04/13 HTML / CSS
Python爬虫之爬取二手房信息
2021/04/27 Python
CPU不支持Windows11系统怎么办
2021/11/21 数码科技
python 远程执行命令的详细代码
2022/02/15 Python
Java处理延时任务的常用几种解决方案
2022/06/01 Java/Android