Python Web编程 - WSGI

WSGI 是服务器与应用程序通信的接口规范,在 PEP-3333 提出,如果一个 Web 框架基于 WSGI 规范编写,那么它就可以运行在任意支持 WSGI 规范的 Web 服务器上,httpdnginx 以及 Python 标准库中的 wsgiref 等 Web 服务器都支持 WSGI 规范,而 djangoflask 这些基于 WSGI 的 Web 框架也都能够运行在这些服务器上

编写应用程序

WSGI 应用程序有以下几点要求

  • 需要是一个 callable 对象
  • 接受两个参数,环境字典、还有一个向 Web 服务器返回状态码和响应头部的函数
  • 返回值为一个可迭代对象

一个简单的例子

from wsgiref.simple_server import make_server

def easy_app(env, resp):
resp_headers = [('Content-Type', 'text/html')] #头部需要用列表嵌套元组的格式
status = '200 OK'
resp(status, resp_headers) #resp 函数接受 状态码和头部
return [b'<h1>hello, world</h1>'] #返回一个可迭代对象,并且 body 必须转为 ascii 码


if __name__ == '__main__':
server = make_server('localhost', 8080, easy_app) #这里使用 python 自带的 wsgi 服务器
server.serve_forever()

测试

$ curl localhost:8080
<h1>hello, world</h1>

操作环境变量

这个例子返回当前这次请求的一些环境变量

from wsgiref.simple_server import make_server


def application(env, resp):
resp_body = ['{}:{}'.format(key, val) for key, val in env.items() if
key in ('SERVER_PROTOCOL', 'CONTENT_TYPE', 'HTTP_HOST', 'USER')]
#由于变量太多,只返回这几个就行了
resp_body = '\n'.join(resp_body) #格式化以下返回 body

status = '200 OK'
resp_headers = [
('Content-Type', 'text/plain'),
('Content-Length', str(len(resp_body))) #增加 Length 字段
]
resp(status, resp_headers)

return [resp_body.encode('ascii')]


if __name__ == '__main__':
server = make_server('localhost', 8080, application)
server.serve_forever()

测试

...
> server.serve_forever()
127.0.0.1 - - [18/Mar/2017 10:11:01] "GET / HTTP/1.1" 200 88

启动之后通过 curl 测试,返回了我们指定的几个环境变量

$ curl localhost:8080
USER:anyisalin
HTTP_HOST:localhost:8080
SERVER_PROTOCOL:HTTP/1.1
CONTENT_TYPE:text/plain

表单解析

如果我们需要解析表单并处理,看下面这个例子

from wsgiref.simple_server import make_server
from urllib.parse import parse_qs


def get_form(env): #写一个将查询参数转换为字典的函数
data = env.get('QUERY_STRING') #环境变量中的 QUERY_STRING 就是表单的参数
if data:
return {key: val[0] for key, val in parse_qs(data).items()}
else:
return {}


def application(env, resp):
form = get_form(env)
if form.get('submitted'): #判断是否是表单提交
try:
name = form.get('name')
resp_html = '<h1>Hello {}</h1>'.format(name)
except Exception as e:
resp('500 ERR', [('Content-Type', 'text/plain')])
return [str(e).encode('ascii')]
else:
resp('200 OK', [('Content-Type', 'text/html')])
return [resp_html.encode('ascii')]
else: #如果不是表单提交,就正常渲染表单
resp('200 OK', [('Content-Type', 'text/html')])
resp_html = '''
<form>
name: <input type="text", name="name"/>
submit: <input type="submit", name="submitted"/>
</form>
'''
return [resp_html.encode('ascii')]


server = make_server('', 8080, application)
server.serve_forever()

测试,点击提交

写不下去了

为什么不用 Web 框架呢

这样写简直 LOW 到爆,没有路由,不够安全、扩展性不够

主要还是写起来真累!

我想要这样写!

@app.route('/', methods=['GET'])
def index():
return '<h1>hello, world</h1>'

@app.route('/hello/<username>')
def hello(username):
return '<h1>hello {}</h1>'.format(username)

嗯,这些都是 Web 框架干的事,处理请求,提供路由 等

如果只是要进行 Web 开发,而不打算深入理解底层框架实现的话,用现成的框架就可以了,Python 有很多牛逼的 Web 框架,FlaskDjangoTornado等,各有各的优点,随便挑选一个开始你的 Web 开发之路吧

嗯,如果你都用不惯的话,也可以自己实现一个