一、WSGI是什么
==================================
WSGI:Web Server Gateway Interface
WSGI :不是Server,不是python模块,不是python框架,也不是用python编写的软件。
WSGI:是一个python标准(协议),由 PEP 3333描述。
它用来规定Web Server如何与python应用程序通信。
如果一个Python应用程序(或框架,如Django)符合WSGI标准,那么它可以在任何符合WSGI标准的Server上运行(比如Apache)。
它对Server端和Python应用程序端都做了规定。
==================================
对Server端的规定:
1、Server必须把http请求以python 字典的形式,作为第一参数传递给python应用程序。这个字典是这样的:
{
'REQUESTMETHOD':'GET', # method
'PATH_INFO':'/path/to/python/application', #URL
'QUERY_STRING':'name=bill&age=10', #参数
'HTTP_ACCEPT':'text/html', #头域
'HTTP_HOST':'www.mydomain.com', #头域
'SERVER_PORT':'8080', #Server 端口号
'REMOTE_ADDR':'192.168.101.2' , #客户端IP地址
.........
}
这个字典内容很多,不一一列举,总之这个字典包含了http请求的所有信息,包括method、URL、参数、头域等,还包含了一些环境信息,比如Server端口号,请求者的ip地址等等。
2、Server必须把用来设置http返回码和http响应头域的回调函数作为第二参数传递给python应用程序。此回调函数接受两个参数,第一个参数是http返回码,以字符串形式给出,如:'200 OK';第二个参数是http响应头,以list形式给出,此list里放的是一个一个的tuple,如: [ ('Content-tpye', 'text'), ('Content-Length', '230') ]
3、Server从应用程序的返回值得到http响应的消息体。这个返回值必须是一个iterator,Server遍历这个iterator,把里面的所有东西依次返回给客户端浏览器。
=======================================
对python应用程序端的规定:
1、应用程序必须是一个callable,这个callable接受上述两个参数,作为第一和第二参数;这个callable返回一个iterator,里面存放消息体。
2、应用程序从Server传来的第一个参数取请求相关的信息。比如Server传来的第一参数叫environ,想取客户端IP,可以这样:
client_ip = environ['REMOTE_ADDR']
3、应用程序利用Server传来的第二个参数为http响应设置返回码和响应头。比如Server传来的第二参数叫set_http,想设置响应码和响应头,可以这样:
set_http('200 OK', [ ('Content-tpye', 'text'), ('Content-Length', '230') ])
4、应用程序把响应消息体以返回值的形式交给Server,这个返回值必须是iterator。比如想返回一个打印Hello World的网页,可以这样:
return [r'<html><body><h1>Hello World<h1></body></html>']
注意:虽然字符串本身就是iterator,但上述例子还是将一个字符组包括在了[ ]内部使其成为一个list。这是因为,把字符串作为iterator,Server将会遍历整个字符串,即一个字符一个字符的给客户端发响应,这样影响性能。如果把整个字符串作为list的一个元素,则Server遍历这个list时,把整个字符串一次性返回给客户端,效率高。
===========================================
二、怎么用WSGI
我们需要做什么:
1、我们写的python应用程序要遵守WSGI对应用程序的规定:即,我们的应用程序是一个callable,接受两个参数,返回一个iterator,如上所述。
2、我们写完符合WSGI规定的应用程序后,把程序安装到符合WSGI规定的服务器上。所谓安装,就是把某个URL跟我们写的某某个python程序对应起来。
我们怎么做:
1、写一个符合WSGI规定的应用程序,举两个例子。
例1:假如我们想写一个程序,让访问我们网站的人看到 Hello WSIG !
那么我的程序如下:
def application(env, set_http):
set_http('200 OK', [('Content-type','text/html'),])
return [r'<html><body><h1>Hello WSGI !</h1></body></html>']
例2:假如我们想写一个程序,让访问我们网站的人看到自己的IP地址
那么我们的程序如下:
def application(env, set_http):
ip = env['REMOTE_ADDR']
set_http('200 OK', [('Content-type','text/html'),])
return [r'<html><body><p>Your IP is : %s</p></body></html>'%ip]
以上两个程序都是符合WSGI规定的。
2、把程序安装到Server上。这里我们以Apache为例,把以上两个例子安装到Apache上。
安装之前要做个说明:Apache通过mod_wsgi来实现WSGI协议,mod_wsgi要求应用程序的callable必须叫 application,如果叫别的名字需要额外配置。这不是WSGI的规定,这是Apache mod_wsgi的规定。
2-1、首先安装mod_wsgi,如果是Apache 2.4 以上的版本,这个mod是默认安装完了的,如果是2.0 或者2.2 版本,需要手动安装。用 yum -y install mod_wsgi 即可安装成功。httpd 2.2 版本安装mod_wsgi的过程中需要注意的是,需要安装httpd-devel 和 python-devel才行,能联网的情况下用yum安装即可,另外python版本要高于2.3
2-2、把 Hell WSGI 这个程序安装在 URL /hello 下,只需要在Apache的主配置文件httpd.conf中加入一行:
WSGIScriptAlias /hello /var/www/wsgi-app/hello.py
然后把hello.py扔到/var/www/wsgi-app 目录下就可以了。重启httpd,访问 xx.xx.xx.xx/hello 看到了Hello WSGI !
2-3、把显示ip地址的程序安装到 URL /ip 下,跟2-2一样,就不写了。
===================================
三、我们不要这么做
如果真像上面那样,每写一个程序都安装一下,太麻烦,我们不这么做。
我们用框架开发web应用程序,只要该框架符合WSGI协议。Django是一个符合WSGI的框架。
我们把Django开发好的一个工程安装到 “ / ” 下,其他的URL交由Django处理,只安装一次即可。
下面以Django Tutorial 中的 poll 程序 为例,说一下怎么把一个Django工程安装到Apache上。
我们在Apache的根目录(/var/www/)下创建mysite目录,把工程文件都拷贝进去。目录结构如下:
然后我们在httpd.conf中加入这2行:
WSGIScriptAlias / /var/www/mysite/mysite/wsgi.py
WSGIPythonPath /var/www/mysite
注意1,wsgi.py是我们在创建工程时(django-admin.py startproject)自动生成的
注意2,跟安装自己写的小程序不同,此处要加入2行。第2行是告诉mod_wsgi,你的工程文件夹是可以import的
在浏览器里输入 xx.xx.xx.xx/polls 验证基本功能可用
在浏览器里输入 xx.xx.xx.xx/admin 发现admin页面打不开,提示:attempt to write a readonly database,把sqlite文件的权限改为777之后,又提示 unable to open database file。在网上找了好久才找打答案:
sqlite 在开始一个事务的时候,需要在数据库文件相同目录下新建一个临时文件,所以,数据库文件所在的路径必须对操作数据库的用户有写权限!把数据库文件以及其所在文件夹的权限都改为 777 后,问题解决。
最后的权限是这样:
/var/www 755
/var/www/mysite 777
/var/www/mysite/sqlite3.db 777
当admin的页面打开后,我们发现样子变了,所有的图标,css都没有,样子很丑。
原因是,当年我们开发polls时,使用的是 manage.py runserver ,这个Django的development server。它会自动的为每个installed app 提供静态文件服务。而diango本身不是不为静态文件提供服务的,所以admin的各种静态文件都无法传到客户端浏览器。
关于静态文件的问题,我们专门写一篇来讨论,这里先不细讲,本篇只关注WSGI。
================== 2015.04.16 更新 ===================================
一、
Server传给应用程序的字典里,以 HTTP_ 开头的都是请求头域,比如 HTTP_ACCEPT_ENCODING,,HTTP_CONNECTION,HTTP_USER_AGENT 等等。
但是注意,CONTENT_TYPE 和 CONTENT_LENGTH 不是以 HTTP_ 开头的。
即 Server 要把用户的请求头里,除了CONTENT_TYPE 和 CONTENT_LENGTH 的,都加上 HTTP_ 前缀再交给 python 应用程序。
二、
Server 把用户请求的消息体,也放到这个字典里,传给应用程序。对应字典里的 key 是:wsgi.input,它的 value 是一个 file-like object。
{
.........
'wsgi.input':a-file-like-object , #请求消息体
}
除了 wsgi.input 还有几个必须出现在字典里的东西,比如 wsgi.errors, wsgi.multithread 等等,由于不常用,这里就不展开说了。
较常用的就是 wsgi.input ,我们的应用程序可以用 env['wsgi.input'].read() 来读取请求消息体,以便后续处理。