类 Flask 框架请求封装| 学习笔记

开发者学堂课程【Python Web 开发基础类 Flask 框架请求封装】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址https://developer.aliyun.com/learning/course/554/detail/7638


类 Flask 框架请求封装


WSGI

类 Flask 框架请求封装| 学习笔记

WSGI 是一套协议它主要规定了 WSGI Server 与 WSGI App 之间是如何调用的,它规范了这个调用接口,那我们刚才已经有所了解了,所以说我们再回到说浏览器,发起的请求当然很多,浏览器不止一个,这边其实有很多 HTTP 请求,也就是说这边可能有很多 tcp 连接。  

那不管怎么说,对我们来说,从应用层角度来讲的话,它全部都是 HTTP 协议的这样一个请求,这个请求被 WSGI 获取到之后,就会对这个请求进行解析,解析之后将这些信息重新封装到一个字典中,这个字典就是 environment,这个字典把它所请求的这些数据装在一个环境数据的字典中,提供给大家使用。这个字典我们有的时候其实习惯了称为 request,也就是它的请求,请求数据,那这个信息我们拿到之后我,这个请求就得转发给你的这个 app,由 app 对这个请求进行处理,那往往会生成一些数据,这些数据最终要返回到浏览器端,但是必须按照 WSGI 的要求来做。

WSGI 在调用你的 app 的时候,会传入两个参数,第一个参数 environment,第二个参数 start response。这两样东西第一个是个字典,第二个呢是一个函数,这个函数拿到之后,在那个函数在 return 或者在向外提交你那个数据的时候,请你给他返回正文之前,请你调用他传进来这个 stata race boss ,这个函数里面一般要传3个信息,但是一般情况下你传2个就够了。

第一个 stater ,也就是说传入的状态码和他的描述第二个是 race boss ,也就是你要响应的头,这往往又是个字典或跟字典相当的,这个二元组组成集合往往都是这样子的。

有的时候也记不清了因为我们慢慢就不用太注重,所以我们想办法把它封装起来。

那这时,我们要处理东西,不管怎么样先调它,调用完之后把你组织好的正文内容放到一个可迭代对象里面去,然后 return 出去,别人要拿到你这个可迭代对象之后,要迭代的,迭代之后要把你这些每一个元素都通过 stp 封装,然后返回回去的,但是你返回的每一个这个封装你你这每一个元素我都要替你封装。

这些就是 WSGI 为我们提供的一套协议,那我们一般来讲遵守这套协议就可以开发了,遵守这套协议是 Python 为我们提供了一个参考的库,真要用的话一般来讲要使用 flask 这样的框架,那我们在学习的时候肯定会有很多障碍,所以我们必须先学 WSGI,以及我们看看他究竟是怎样处理数据的,因为他们代码量太大了,你一时半会都了解不了他到底是怎么处理数据的,很多层次数据在一个流程中不断的跑,跑到这个调用了,又被那个调用了,所以你看不清楚,所以我们就必须来自己写一个。

WSGI 主要规定了服务器端和应用程序间的接口。  

WSGI 服务器——wsgiref  

wsgiref 这个一个 WSGI 参考实现库  

wsgiref.simple_server 模块实现一个简单的 WSGI HTTP 服务器.  

wsgiref.simple_server.make_server(host, port, app, server_class=WSGIServer,  

handler_class=WSGIRequestHandler)启动一个 WSGI 服务器

wsgiref.simple_server.demo_app(environ, start_response)]一个函数,小巧完整的 WSGI 的应用程序的实现  

#返回文本例子  

from wsgiref.simple_server import make_server, demo_app  

ip ='127.0.0.1'  

port = 9999  

server = make_server(ip, port, demo_app)# demo_app 应用程序,可调用  

server.serve_forever()# server.handle_request()执行—次  

WSGI 服务器作用  

监听 HTTP 服务端口(TCPServer,默认端口80)

接收浏览器端的 HTTP 请求并解析封装成 environ 环境数据  

负责调用应用程序,将 environ 和 start_response 方法传入  

将应用程序响应的正文封装成HTTP响应报文返回浏览器端  

WSGI APP 应用程序端  

1、应用程序应该是一个可调用对象  

Python 中应该是函数、类、实现了_call_方法的类的实例  

2、这个可调用对象应该接收两个参数

# 1 函数实现  

def application(environ, start_response): pass  

# 2 类实现  

class Application: def _init_(self, environ, start_response): pass  

# 3 类实现  

class Application: def _call_(self, environ, start_response): pass  

3、以上的可调用对象实现,都必须返回一个可迭代对象

#函数实现  

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):  

return[res_str]

environ和start_response 这两个参数名可以是任何合法名,但是一般默认都是这2个名字。应用程序端还有些其他的规定,暂不用关心

environ  

environ 是包含 Http 请求信息的dict对象  

名称

含义

REQUEST_METHOD

请求方法,GET、POST等

PATH_INFO

URL中的路径部分

QUERY_STRING

查询字符串

SERVER_NAME, SERVER_PORT

服务器名、端口

HTTP_HOST

地址和端口

SERVER_PROTOCOL

协议

HTTP_USER_AGENT

UserAgent信息

start_response  

它是一个可调用对象。有3个参数,定义如下:  

start_response(status, response_headers, exc_info=None) status 是状态码,例如 200 0K

response_headers 是一个元素为二元组的列表,例如[(Content-Type,'text/plain;charset=utf-8')]  

exc_info 在错误处理的时候使用  

start_response 应该在返回可迭代对象之前调用,因为它返回的是 Response Header。返回的可迭代对象是 Response Body.  

服务器端  

服务器程序需要调用符合上述定义的可调用对象 APP,传入 environ、start_response ,APP 处理后,返回响应头和可迭代对象的正文,由服务器封装返回浏览器端。

#返回网页的例子  

from wsgiref.simple_server import make_server  

def application(environ, start_response):  

status ='200 OK'  

headers =[('Content-Type",'text/html;charset=utf-8')]  

start_response(status, headers)  

#返回可迭代对象  

html ='

马哥教育欢迎你

".encode("utf-8")  

return [html]  

ip ='127.0.0.1'  

port = 9999  

server = make_server(ip, port, application)

server.serve_forever()# server.handle_request()一次  

simple_server 只是参考用,不能用于生产。  

测试用命令

$ curl -I http://192.168.142.1:9999/xxx?id=5|  

$ecurl -x POST http://192.168.142.1:9999/yyy -d'{"x":2}'  

使用 HEAD 方法

指定方法,-d 传输数据  

到这里就完成了一个简单的 WEB 程序开发。  

WEB 服务器  

•本质上就是一个 TCP 服务器,监听在特定端口上  

•支持 HTTP 协议,能够将 HTTP 请求报文进行解析,能够把响应数据进行 HTTP协议的报文封装并返回浏览器 端.  

•实现了 WSGI 协议,该协议约定了和应用程序之间接口(参看 PEP333,https://www.python.org/dev/peps/p ep-0333/)  

APP应用程序

•遵从 WSG 协议  

•本身是一个可调用对象  

•调用 start_response,返回响应头部  

•返回包含正文的可迭代对象  

为了更好的理解 WSGI 框架的工作原理,现在开始动手自己写一个 WEB 框架。

类 Flask 框架实现  

从现在开始,我们将一步步完成一个WSGI的WEB框架,从而了解WEB框架的内部机制。  

WSGI 请求 environ 处理  

WSGI 服务器程序会帮我们处理 HTTP 请求报文,但是提供的 environ 还是一个用起来不方便的字典  

http://127.6.0.1:9999/python/index.htm1?id=1234&name=tom  

('SERVER_PROTOCOL','HTTP/1.1')  

('wsgi.url_scheme",'http')  

('HTTP_HOST','127.0.0.1:9999')  

('SERVER_PORT','9999')  

('REMOTE_ADDR','127.0.0.1')  

('REQUEST_METHOD','GET')  

('CONTENT_TYPE','text/plain') ('PATH_INFO",'/python/index.html')  

('QUERY_STRING','id=1234&name=tom') ('HTTP_USER_AGENT','Mozilla/5.θ(Windows NT 6.1) ApplewebKit/537.36 (KHTML, like Gecko) Maxthon/5.0 Chrome/55.0.2883.75 Safari/537.36')

QUERY_STRING 查询字符串的解析  

WSGI 服务器程序处理过 HTTP 报文后,返回一个字典,可以得到查询字符串('QUERY_STRING", 'id=12348name=tom')。这个键值对用起来不方便。

1、编程序解析  

# id=5&name=wayne  

qstr = environ.get('QUERY_STRING')  

print(qstr)  

if qstr:  

for pair in qstr.split('&'):  

k,_,v= pair.partition('=')  

print("k={}, v={}".format(k,v))  

# id=5&name=wayne  

querystr = environ.get('QUERY_STRING')  

if querystr:  

querydict ={k:v for k,_,v in map(lambda item: item.partition('='), querystr.split('&'))} print(querydict)

2、使用 cgi 模块  

# id=5&name=wayne  

qstr= environ.get('QUERY_STRING')  

print(qstr)  

print(parse_qs(qstr))  

#{'name":['wayne"],'id":['5']}  

可以看到使用这个库,可以解析查询字符串,请注意value是列表,为什么? 这是因为同一个key可以有多个值。  

cgi模块过期了,建议使用urllib

3、使用 urllib 库  

#http://127.0.0.1:9999/?id=5&name=wayne&age=&commek=1,a,c&age=19&age=28  

qstr = environ.get('QUERY_STRING')  

print(qstr)  

print(parse.parse_qs(qstr))#字典  

print(parse.parse_qs1(qstr))#二元组列表  

#运行结果  

id=5&name=wayne&age=&comment=1,a,c&age=19&age=20  

{'name":['wayne'],'age":['19','26'],'id':['5'],'comment':['1,a,c']}  

[('id','5'),('name','wayne'),('comment",‘1,a,c'),('age','19'),('age','20')]  

parse_qs函数,将同一个名称的多值,保存在字典中,使用了列表保存。  

comment=1,a,c 这不是多值,这是一个值。  

age 是多值。  

environ 的解析——webob库

环境数据有很多,都是存在字典中的,字典的存取方式没有对象的属性访问方便。  

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

webob 简介  

Python 下,可以对 WSGI 请求进行解析,并提供对响应进行高级封装的库。  

$ pip install webob  

官网文档 docs.webod.org  

webob.Request 对象  

将环境参数解析并封装成 request 对象  

GET 方法,发送的数据是 URL 中 Query string,在 Request Header 中。 request.GET 就是一个字典 MultiDict,里面就封装着查询字符串。

POST 方法,“提交”的数据是放在 Request Body 里面,但是也可以同时使用 Query String. request.POST 可以获取 Request Body 中的数据,也是个字典 MultiDict。

不关心什么方法提交,只关心数据,可以使用 request.params,它里面是所有提交数据的封装,。

request = webob.Request(environ)  

print(request.headers)#类字典容器  

print(request.method)  

print(request.path)  

print(request.query_string)#查询字符串  

print(request.GET)# GET 方法的所有数据  

print(request.POST)# POST 方法的所有数据  

print('params ={}'.format(request.params))#所有数据,参数

MultiDict  

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

from webob.multidict import MultiDict  

md = MultiDict()  

md.add(1,'magedu')  

md.add(1,'.com'】  

md.add('a', 1)  

md.add('a', 2)  

md.add('b','3')  

md['b']='4 for pair in  

md.items(): print(pair) print(md.getall(1)】  

#print(md.getone('a'))#只能有一个值  

print(md.get('a'))#返回一个值  

print(md.get('c'))#不会抛异常 KeyError,返回 None

上一篇:利用HTTP Cache来优化网站


下一篇:linux/linux学习笔记-常用命令(mooc)