WSGI     Web服务器网关接口

WSGI主要规定了Web服务器如何与Web应用程序进行通信,以及如何将Web应用程序链接在一起来处理一个请求。

wsgiref  Python中的WSGI参考模块

一、WSGI 应用程序端:

1、 根据WSGI定义,应用程序应该是可调用对象

2、该可调用对象必须有两个固定参数:environ、start_response

一个是含有服务器环境变量的字典,另一个是可调用对象,该对象使用HTTP状态码和会返回给客户端的HTTP头来初始化响应

environ 变量包含一些熟悉的环境变量,如HTTP_HOST,HTTP_USER_AGENT,REMOTE_ADDR,REQUEST_METHOD,SERVER_PORT,部分如下:

Hello world!

GATEWAY_INTERFACE = 'CGI/1.1'
HTTP_ACCEPT = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8'
HTTP_ACCEPT_ENCODING = 'gzip, deflate, br'
HTTP_ACCEPT_LANGUAGE = 'zh-CN,zh;q=0.9,en;q=0.8'
HTTP_CONNECTION = 'keep-alive'
HTTP_HOST = '127.0.0.1:9999'
HTTP_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36'
QUERY_STRING = ''
REMOTE_ADDR = '127.0.0.1'
REQUEST_METHOD = 'GET'
SERVER_PORT = '9999'
SERVER_PROTOCOL = 'HTTP/1.1'
SERVER_SOFTWARE = 'WSGIServer/0.2'

3、这个可调用对象必须返回一个可迭代对象用于组成响应

res_str = b'github.com\n'

# 函数实现
def application(environ, start_response):
return [res_str] # 类实现
class Application:
def __init__(self, environ, start_response):
pass
def __iter__(self):
yield res_str # 类实现
class Application:
def __call__(self, environ, start_response):
retur [res_str]

  

wsgiref参考库中有以下几个子模块:

* util -- 一些有用的功能和包装

* headers -- 管理响应头

* handlers -- 为server/gateway实现如何处理的基类

* simple_server -- 实现一个简单的WSGI HTTP服务器

* validate -- 位于应用程序和server之间检测错误的校验包装

二、WSGI HTTP Server端的使用

1. 启动一个简单的WSGI HTTP Server:

# 简单web 1
from wsgiref.simple_server import make_server def demo_app(environ, start_response): #copy自simple_server模块
from io import StringIO
stdout = StringIO()
print("Hello world!", file=stdout)
print(file=stdout)
h = sorted(environ.items())
for k, v in h:
print(k, '=', repr(v), file=stdout)
start_response("200 OK", [('Content-Type', 'text/plain; charset=utf-8')])
return [stdout.getvalue().encode("utf-8")] ip = '127.0.0.1'
port = 9999
server = make_server(ip, port, demo_app)
server.serve_forever() server.server_close()


wsgiref.simple_server.make_server(host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler)
通过这个函数可以启动一个用于简单访问的WSGI参考服务器,必须传入host, port, app三个参数。

在运行这段程序之后,就已经实现了一个监听在9999端口的webServer,下面是服务端运行状态和浏览器中访问结果:

访问 http://127.0.0.1:9999/

#server端运行状态:
127.0.0.1 - - [26/Dec/2017 15:01:13] "GET / HTTP/1.1" 200 2128
127.0.0.1 - - [26/Dec/2017 15:01:13] "GET /favicon.ico HTTP/1.1" 200 2096 #浏览器访问结果:
Hello world! Apple_PubSub_Socket_Render = '/private/tmp/com.apple.launchd.Gx10g4snot/Render'
CLICOLOR = '1'
CONTENT_LENGTH = ''
CONTENT_TYPE = 'text/plain'
GATEWAY_INTERFACE = 'CGI/1.1'
GREP_OPTIONS = '--color=auto'
HOME = '/Users/ihoney'
HTTP_ACCEPT = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8'
HTTP_ACCEPT_ENCODING = 'gzip, deflate, br'
HTTP_ACCEPT_LANGUAGE = 'zh-CN,zh;q=0.9,en;q=0.8'
HTTP_CONNECTION = 'keep-alive'
HTTP_HOST = '127.0.0.1:9999'
HTTP_UPGRADE_INSECURE_REQUESTS = '1'
HTTP_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36'
LC_CTYPE = 'zh_CN.UTF-8'
......

  

2. 自定义响应的网页内容:

# 简单web 2
from wsgiref.simple_server import make_server def application(environ:dict,start_response):
# print(type(environ),environ)
html = "<h1>北京欢迎你</h1>"
# start_response("200 OK", [('Content-Type','text/plain; charset=utf-8')]) #文本格式
start_response("200 OK", [('Content-Type', 'text/html; charset=utf-8')]) #html格式 return [html.encode()] ip = '127.0.0.1'
port =9999
server = make_server(ip,port,application)
server.serve_forever() server.server_close() #运行结果:
127.0.0.1 - - [26/Dec/2017 15:38:55] "GET / HTTP/1.1" 200 24
127.0.0.1 - - [26/Dec/2017 15:38:55] "GET /favicon.ico HTTP/1.1" 200 24

  浏览器访问结果:

simple_server 只是参考,不可用于生产环境。

三、QUERY_STRING 查询字符串的解析

1. 使用cgi模块:

# 简单web 3 使用cgi模块解析query_string
import cgi
from wsgiref.simple_server import make_server def application(environ:dict,start_response):
qstr = environ.get("QUERY_STRING")
print(qstr)
# ?id=5&name=ihoney&age=18,19
print(cgi.parse_qs(qstr)) #字典,value为列表类型
print(cgi.parse_qsl(qstr)) #二元组列表 html = "<h1>北京欢迎你</h1>"
start_response("200 OK", [('Content-Type', 'text/html; charset=utf-8')])
return [html.encode()] ip = '127.0.0.1'
port =9999
server = make_server(ip,port,application)
server.serve_forever() server.server_close() #浏览器访问http://127.0.0.1:9999/?id=5&name=ihoney&age=18,19
#运行结果:
127.0.0.1 - - [26/Dec/2017 15:51:17] "GET /?id=5&name=ihoney&age=18,19 HTTP/1.1" 200 24
id=5&name=ihoney&age=18,19
{'age': ['18,19'], 'name': ['ihoney'], 'id': ['5']}
[('id', '5'), ('name', 'ihoney'), ('age', '18,19')]

  在写的时候IDE工具就会提示CGI模块已经过期了,建议使用urllib库。

2. 使用urllib库

# 简单web 4 使用urllib模块解析query_string
from urllib import parse
from wsgiref.simple_server import make_server def application(environ:dict,start_response):
qstr = environ.get("QUERY_STRING")
print(qstr)
# ?id=5&name=ihoney&age=18,19
print(parse.parse_qs(qstr)) #字典,value为列表类型
print(parse.parse_qsl(qstr)) #二元组列表 html = "<h1>北京欢迎你</h1>"
start_response("200 OK", [('Content-Type', 'text/html; charset=utf-8')])
return [html.encode()] ip = '127.0.0.1'
port =9999
server = make_server(ip,port,application)
server.serve_forever() server.server_close() #浏览器访问:http://127.0.0.1:9999/?id=5&name=ihoney&age=18,19
#运行结果:
id=5&name=ihoney&age=18,19
{'id': ['5'], 'age': ['18,19'], 'name': ['ihoney']}
[('id', '5'), ('name', 'ihoney'), ('age', '18,19')]
127.0.0.1 - - [26/Dec/2017 15:58:40] "GET /?id=5&name=ihoney&age=18,19 HTTP/1.1" 200 24

  

3. 使用第三方库webob

pip3 install webob

第三方库webob可以把环境数据的解析封装成对象,使用时直接调用。

webob.request module:

req.method: 请求方法
req.GET: 返回一个类字典对象,GET请求方式提交的查询字符串的二元组格式。
req.POST: 也返回一个类字典对象,POST请求正文的查询字符串。一般是表单提交
req.params: 一个类字典对象,包括GET和POST的所有查询字符串。
req.body: POST提交的请求正文的内容
req.cookies: 字典格式的所有cookie
req.headers: 包含所有请求头的字典,不区分大小写

web.response module:

response.status: 响应码加描述信息,如"200 OK"
response.status_code: 响应码,只有 "200"
response.headerlist: 所有响应头的列表,如"[('Content-Type', 'text/html')]"
response.app_iter: 一个可迭代对象(如列表和生成器),用于产生响应的内容
response.content_type: 响应内容的类型,如"text/html","text/plain"
response.charset: 字符集编码类型
response.set_cookie(name=None, value='', max_age=None, path='/', domain=None, secure=False, httponly=False, comment=None, overwrite=False): 为客户端设置一个cookie,max_age控制cookie的有效时长,以秒为单位
response.delete_cookie(key, path='/', domain=None): 从客户端删除一个cookie
response.cache_expires(seconds=0): 设置这个响应的缓存时间,单位为秒,如果seconds为0表示这个响应不缓存
response(environ, start_response): 返回对象是一个WSGI应用程序的响应

3.1 webob.Request

#简单web 5,使用第三方库webob解析
from wsgiref.simple_server import make_server
from webob import Request, Response def application(environ: dict, start_response):
request = Request(environ)
print(request.method)
print(request.path)
print(request.GET)
print(request.POST)
print(request.params)
print(request.query_string) html = "<h1>北京欢迎你</h1>"
start_response("200 OK", [('Content-Type', 'text/html; charset=utf-8')])
return [html.encode()] ip = '127.0.0.1'
port = 9999
server = make_server(ip, port, application)
server.serve_forever() server.server_close() #浏览器访问:http://127.0.0.1:9999/index.html?id=5&name=tom,jerry&age=17&age=18,19
#运行结果:
GET
/index.html
GET([('id', '5'), ('name', 'tom,jerry'), ('age', '17'), ('age', '18,19')])
<NoVars: Not a form request>
NestedMultiDict([('id', '5'), ('name', 'tom,jerry'), ('age', '17'), ('age', '18,19')])
id=5&name=tom,jerry&age=17&age=18,19
127.0.0.1 - - [26/Dec/2017 16:51:41] "GET /index.html?id=5&name=tom,jerry&age=17&age=18,19 HTTP/1.1" 200 24

  

3.2 webob.Resphone

#
from wsgiref.simple_server import make_server
from webob import Request, Response def application(environ: dict, start_response):
res = Response("<h1>北京欢迎你</h1>")
return res(environ,start_response) #__call__ ip = '127.0.0.1'
port = 9999
server = make_server(ip, port, application)
server.serve_forever() server.server_close() #浏览器访问:http://127.0.0.1:9999/index.html?id=5&name=tom,jerry&age=17&age=18,19
#运行结果:
127.0.0.1 - - [26/Dec/2017 18:08:03] "GET /index.html?id=5&name=tom,jerry&age=17&age=18,19 HTTP/1.1" 200 24

  

3.3 MultiDict

MultiDict允许一个key存好几个值。

Request.GET、Request.POST 都是MultiDict字典

# multidict
from webob.multidict import MultiDict md = MultiDict()
md[1] = 'b'
md.add(1,'a') print(md.get(1)) #只返回一个值
print(md.getall(1))
# print(md.getone(1)) #要求key的value只能有一个,否则抛KeyError异常
print(md.get('c')) #不存在返回默认值None
#运行结果:
a
['b', 'a']
None

 

3.4 webob.dec.wsgify 装饰器

官方文档:https://docs.pylonsproject.org/projects/webob/en/stable/api/dec.html

功能:将一个函数变成一个WSGI应用程序

使用举例:

from webob.dec import wsgify

@wsgify
def myfunc(req):
return webob.Response('hey there')

  wsgi装饰器装饰的函数应该具有一个参数,这个参数是webob.Request类型,是对字典environ的对象化后的实例。返回值必须是一个webob.Respnose类型,所以在函数中应该创建一个webob.Response类型的实例。

# wsgify
from wsgiref.simple_server import make_server
from webob import Request, Response,dec def application(environ: dict, start_response):
res = Response("<h1>北京欢迎你</h1>")
#200 OK
#[('Content-Type', 'text/html; charset=UTF-8'), ('Content-Length', '0')]
return res(environ,start_response) #__call__ @dec.wsgify
def app(request:Request) -> Response:
return Response("<h1>Welcome to BeiJing</h1>") ip = '127.0.0.1'
port = 9999
# server = make_server(ip, port, application)
server = make_server(ip, port, app)
server.serve_forever() server.server_close() #浏览器访问:http://127.0.0.1:9999/index.html?id=5&name=tom,jerry&age=17&age=18,19
#运行结果:
127.0.0.1 - - [26/Dec/2017 20:25:14] "GET /index.html?id=5&name=tom,jerry&age=17&age=18,19 HTTP/1.1" 200 27
127.0.0.1 - - [26/Dec/2017 20:25:14] "GET /favicon.ico HTTP/1.1" 200 27

  

改进:

from wsgiref.simple_server import make_server
from webob import Request, Response,dec @dec.wsgify
def app(request:Request) -> Response:
return Response("<h1>Welcome to BeiJing.</h1>") if __name__ == "__main__":
ip = '127.0.0.1'
port = 9999
server = make_server(ip, port, app)
try:
server.serve_forever()
except KeyBoardInterrupt:
pass
finally:
server.server_close()

  

3.5  webob.Response SourceCode

    def __call__(self, environ, start_response):
"""
WSGI application interface
"""
if self.conditional_response:
return self.conditional_response_app(environ, start_response) headerlist = self._abs_headerlist(environ) start_response(self.status, headerlist)
if environ['REQUEST_METHOD'] == 'HEAD':
# Special case here...
return EmptyResponse(self._app_iter)
return self._app_iter

  

Chrome插件Postman POST 提交:

总结:

本文简单介绍了WSGI、WSGI HTTP Server、查询字符串的处理、第三方库webob的一些用法。

  

[Python WEB开发] 使用WSGI开发类Flask框架 (二)的更多相关文章

  1. python Web开发之 WSGI & uwsgi & uWSGI

    首先弄清下面几个概念: WSGI 全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述web server ...

  2. Python Web 应用:WSGI基础

    在Django,Flask,Bottle和其他一切Python web 框架底层的是Web Server Gateway Interface,简称WSGI.WSGI对Python来说就像 Servle ...

  3. Python自动化运维之30、Flask框架

    Flask 官网:http://flask.pocoo.org/ flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是 ...

  4. 我的第一个python web开发框架(26)——定制ORM(二)

    弄完底层数据库操作模块后,接下来要做的是ORM的正式设计.在开始之前,我们需要思考一下怎么来设计一个ORM呢?这个类它能帮助我们处理什么样的问题?需要有哪些功能模块?怎么做到针对不同的数据库与表单进行 ...

  5. Python之Flask框架二

    今天接着上一篇继续写一篇关于flask的随笔. 本文大纲: 1.获取请求参数 2.一个函数处理多个请求方式 3.重定向 4.错误响应 5.全局错误处理 6.返回json格式数据 7.自定义返回内容状态 ...

  6. Flask框架(二):路由与蓝图

    一.路由 使用 route() 装饰器来把函数绑定到 URL: @app.route('/') def index(): return 'Index Page' @app.route("/h ...

  7. 基于Flask框架的Python web程序的开发实战 <一> 环境搭建

    最近在看<Flask Web开发基于Python的Web应用开发实战>Miguel Grinberg著.安道译 这本书,一步步跟着学习Flask框架的应用,这里做一下笔记 电脑只安装一个P ...

  8. virtualenv 环境下 Nginx + Flask + Gunicorn+ Supervisor 搭建 Python Web

    在这篇文章里,我们将搭建一个简单的 Web 应用,在虚拟环境中基于 Flask 框架,用 Gunicorn 做 wsgi 容器,用 Supervisor 管理进程,然后使用 Python 探针来监测应 ...

  9. Python Flask框架

    Python有很多Web框架,可谓是百家争鸣,我这里列出几个比较叼的几个框架 Django      市场占有率最高,官方文档几近完美,但是适合比较大的项目,小项目会显得累赘. Tornado    ...

随机推荐

  1. 撩课-Python-每天5道面试题-第7天

    一. 函数的返回值的概念,语法以及注意事项? 场景 当我们通过某个函数, 处理好数据之后, 想要拿到处理的结果 语法 def 函数(): 函数体 return 数据 注意事项 3.1 return 后 ...

  2. High Performance MySQL笔记:count

    在SQL中使用count()好像是非常自然的事情: SELECT COUNT(*) FROM TABLE_NAME; 有时候确实会想过,count(*)和单独的count(column_name)有什 ...

  3. python保存字典和读取字典pickle

    import pickle import numpy as np def save_obj(obj, name): with open(name + '.pkl', 'wb') as f: pickl ...

  4. UOJ169. 【UR #11】元旦老人与数列

    传送门 考虑用 \(segment~tree~beats\) 那一套理论,维护区间最小值 \(mn\) 和严格次小值 \(se\) 那么可以直接 \(mlog^2n\) 维护前三个操作 考虑维护历史最 ...

  5. NodeList、HTMLCollection和NamedNodeMap

    上篇文章以arguments为例讲到了类数组对象,这篇我们讨论更多的类数组对象NodeList.HTMLCollection和NamedNodeMap.既然是类数组对象,这3种对象也都能应用上篇文章中 ...

  6. JS全国城市三级联动

    HTML <select id="s_province" name="s_province"></select> <select ...

  7. Angular1.x 之Providers (Value, Factory, Service and Constant )

    官方文档Providers Each web application you build is composed of objects that collaborate to get stuff do ...

  8. ubuntu16下面 redis 无法链接到客户端问题

    1.今天从github上面下载了一个项目,链接到自己的tomcat里面的redis,结果在虚拟机里面可以链接成功,但是在客户端总是提示链接失败.google之后,原来是 因为 需要在redis里面设置 ...

  9. 学习笔记:IIS搭建PHP网站出现404错误的解决办法

    关于404错误提示相信大家都遇到过吧,记得我遇到这个问题的时候,弄得我焦头烂额的,今天给大家分享下,使用IIS大家PHP网站时出现404错误提示的处理方法,希望对各位朋友有所帮助.IIS搭建PHP出现 ...

  10. 【 PostgreSQL】十条实用数据库SQL优化建议

    基于PostgreSQL,总结几条常用的查询操作的优化建议,部分也适用于Oracle等数据库. 1.选择合适的分布键 分布键选择不当会导致重分布.数据分布不均等,而数据分布不均会使SQL集中在一个se ...