WEB服务Flask第二天
一、路由、request与response
1. 1 路由规则
简单使用路由案例:
@blue.route('/find')
def find():
return ""
请求的url是 http://localhost:5000/find。路由中配置的路径即是请求的path路径。
如果使用"蓝图"则在注册时,可以注明它的url前辍。
app.register_blueprint(bank.blue, url_prefix="/bank")
通过url_prefix参数指定某一个模块下的所有请求资源,对于/find路由来说的,应该是/bank的子路径(路由),访问全路径是 http://localhost:5000/bank/find。
1.1.1 路由的path中参数
语法:
@app.route('/find/<converter:word>', methods=['GET'])
def find(word):
return ""
converter 是参数的转换器, 一般是指定的类型,如string, **int, float,path **,uuid, any。
其中any()表较特殊,可以指定任意的类型转换器, 如 <any(int, string, uuid): word>, 但目前最新flask版本不支持any()动作,直接写成"/find//". word表示任意类型。
另外,path转换器主要用于引用别的网址时使用。
@app.route('/forward/<path:url>')
def forward(url):
# 重定向url
return redirect(url)
以上的路由配置,对于"/forward/http://www.baidu.com" 路径是合法,如果将path转换器换成string有可能会出错。
另外,path路径中可以接收多个参数
@blue.route('/add/<bankName>/<int:page>')
def addCard(bankName, page):
return "%s 开户成功! 在第 %s 页" % (bankName, page)
1.1.2 路由的请求方法
路由中的请求方法是通过methods设置的, 且要求是list类型。
@app.route('/find/<word>')
def forward(word):
# keyword有可能是银行的id, 名称, 地址
return "keyword 是str类型, 值是 %s" %( keyword)
上面的注册路由是没有指定methods, 默认中包含GET和OPTIONS。
常见的请求方法:
- GET 查询数据使用, 可以上传的参数大小限于1M以内, 参数是显示在请求地址中的。
- POST 添加或编辑数据时使用, 可以上传超过1G的大数据, 且以表单参数的方式上传,并不显示请求地址中, 相比于GET,请求参数比较安全且支持大数据或文件。
- PUT 更新数据时使用, 局部数据的修改,如修改用户的口令或头像
- PATCH 批量更新数据时使用, 整体的数据修改,如修改用户的收货地址、实名认证、绑定银行卡。
- DELETE 删除数据操作时使用。
- 其它的方法: OPTIONS、HEAD
如声明一个处理函数,用于删除银行信息,正确的路由配置如下:
@app.route('/del/<int:bank_id>', methods=["DELETE"])
def delete4id(bank_id):
return "删除操作成功!"
针对PUT/DELETE/POST/GET等url接口测试,可以使用requests库
pip install requests
requests库中提供相关函数,函数的名称与请求方法是一一对应,当然也可以使用requests.request()方法是最全的方法,其它的函数都是在request()方法之上重新封装的。
url = "http://localhost:5000/bank/del/100"
method='DELETE'
resp = requests.request(method, url)
print(resp.text)
也可以使用请求方法对应的requests库的相关函数,如:
url = "http://localhost:5000/bank/del/100"
method='DELETE'
resp = requests.delete(url)
print(resp.text)
1.1.3 路由的反向解析
from flask import url_for
from flask import Blueprint
blue = Blueprint("cardBlue", __name__)
@blue.route('/add/<bankName>')
def add(bankName):
...
@blue.route('/select')
def select():
return "<a href=%s>进入下一个页面</a>" % url_for('cardBlue.add', bankName="中国银行")
url_for(“函数名”, **kwargs) 反向解析获取flask的路由注册的路径
url_for(‘蓝图名.函数名’, **kwargs) 反向解析指定蓝图下的路由注册的路径
@blue.route('/add/<bankName>/<int:page>')
def addCard(bankName, page):
return """
%s 开户成功! 在第 %s 页
<br>
<a href="%s">返回首页</a>
""" % (bankName, page, url_for('index'))
@app.route("/")
def index():
return """
<ul>
<li> <a href="%s">银行卡开户</a></li>
<li> <a href="%s">银行管理</a></li>
<li> <a href="%s">用户管理</a></li>
</ul>
""" %(
url_for('cardBlue.addCard', bankName="中国银行", page=3),
url_for('bankBlue.edit', bankId=1),
url_for('userBlue.find'),
)
1.2 请求对象
from flask import request
请求对象本质上是封装客户端发送的请求数据,在Flask中由Werkzeug库(实现Python的WSGI接口)封装的, 包含请求的路径、请求的方法、请求的头、请求中包含的Cookie、请求的参数及上传的数据。
一个请求对象中包含数据的属性一般都是dict类型。如:
- request.args 查询参数, url路径中使用?分隔的查询参数
- request.form 表单参数, 一般是post请求方法中包含的数据
- request.headers 请求头
- request.cookies Cookie数据
- request.files 上传的文件
- request.method 请求方法, 且大写字母表示的
- request.url 请求的路径(完整路径), 如http://localhost:5000/find?id=1&name=disen。
- request.remote_addr 远程访问的客户端IP地址
<ul>
<li>url: {{ request.url }}</li>
<li>base_url: {{ request.base_url }}</li>
<li>host_url: {{ request.host_url }}</li>
<li>path: {{ request.path }}</li>
<li>method: {{ request.method }}</li>
<li>客户端IP: {{ request.remote_addr }}</li>
<li>请求头: {{ request.headers }}</li>
<li>Cookie: {{ request.cookies }}</li>
</ul>
@blue.route('/find', methods=['GET'])
def find():
print('url', request.url)
print('base_url', request.base_url)
print('host_url', request.host_url)
print('path', request.path)
print('headers', request.headers)
print('cookie', request.cookies)
return render_template("user_list.html", request=request)
1.3 响应对象response
在服务端,当业务处理完成后,生成响应的数据并封装成响应对象,并传给Python的WSGI, 由WSGI向客户端发送数据流。
-
直接返回文本和状态码
flask的处理函数如果直接返回文本或附带一个响应的状态码,则会自动封装一个简单Response对象,且数据类型默认为: text/html;charset=utf-8
@blue.route('/publish', methods=['POST']) def publish_bank(): return '{"id": 101, "age": 20}', 200
如果返回是一个html文本数据,可以使用render_template()函数,将写好的html模板经过渲染之后生成的html返回会更好些。
-
使用make_response(data, code) 生成response对象
这种方式是较为常用的方式,通过生成的response响应对象,可以设置响应的header相关的信息。
@blue.route('/publish', methods=['POST']) def publish_bank(): data = '{"id": 101, "age": 20}' code = 200 # 将数据和响应的状态码封装到response对象中 response = make_response(data, code) # 根据数据的类型,设置响应头 response.headers['Content-Type'] = 'application/json;charset=utf-8' return response
-
jsonify()快速生成json响应对象
此函数返回也是一个response对象,只不过response对象的headers已经设置了content-type属性为application/json。
@blue.route('/publish', methods=['POST']) def publish_bank(): data = '{"id": 101, "age": 20}' code = 200 return jsonify(data, code)
-
Response类生成响应对象
@blue.route('/publish', methods=['POST']) def publish_bank(): data = '{"id": 101, "age": 20}' code = 200 # Response(response=None,status=None, # headers=None, mimetype=None,content_type=None, # direct_passthrough=False) response = Response(data, code, content_type="application/json") return response
创建Response对象方法中,可以省略code, 同时也可以将content_type改为mimetype。mimetype表示文件的数据类型, 和content_type表示含义相同。
-
重定向
在一个请求中,由于业务处理的要求,在处理业务之后,需要进入新的页面。而这个页面之前已声明它的路由,则需要使用重定向的方式进入到下一个页。
【注意】重定向也是响应对象,必须要返回。而且相对于浏览器或客户端再次发送新的请求。
@blue.route('/list') def listCard(): return render_template('card/list.html')
@blue.route('/add', methods=['GET', 'POST']) def addCard(): if request.method == "POST": # 接收开户信息 bankId = request.form.get('bank') username = request.form.get('username') phone = request.form.get('phone') # 验证数据是否为空 # 假如操作成功 # 进入列表页面中 return redirect(url_for('bankBlue.listCard')) # GET请求,响应是开户页面 return render_template('card/add.html')
<h3>开户页面</h3> <form action="/bank/add" method="post"> <select name="bank"> <option value="0">--请选择银行--</option> <option value="1">中国银行</option> <option value="2">中国工商银行</option> </select> <br><input name="username" size="20" placeholder="用户名"></input> <br><input name="phone" size="20" placeholder="手机号"></input> <button>提交</button> </form>
1.4 请求异常处理
请求异常,在请求处理过程中,验证某一数据出现的错误,可以中断请求。如果请求异常不是请求数据而引起,或者说请求资源不存在或服务器发生异常,此时可以捕获异常。
1.4.1 abort()中断
在请求处理函数中,可以直接调用abort()函数,中断业务处理。
from flask import abort
@blue.route('/add', methods=['GET', 'POST'])
def addCard():
if request.method == "POST":
# 接收开户信息
phone = request.form.get('phone')
if phone == '17791692095':
# 中断业务
# abort(403) # 发出一个异常响应码
abort(Response("<h3 style='color:red'>%s 当前手机号不能被注册</h3>" % phone, 403))
# GET请求,响应是开户页面
return render_template('card/add.html')
abort()两种写法:
- abort(status_code)
- abort(Response(data, code, headers, …))
####1.4.2 捕获请求异常
通过相关的状态码,获取请求异常,并指定处理函数来响应异常的信息。
@app.errorhandler(404) # 指定状态码捕获
def notfounded(error):
print(error)
return render_template('404.html')
建议处理异常的函数写在与app对象同一个脚本中。
如果处理业务中抛出相关的异常或发生500的异常,则可以指定异常类捕获
@blue.route('/list')
def listCard():
raise Exception('抛出自己的业务异常')
# abort(500)
return render_template('card/list.html')
@app.errorhandler(Exception)
def handlerOfError(error):
return render_template('500.html')
二、Cookie与Session技术
2.1 Cookie存储技术
Cookie数据存储技术,它的数据存储在客户端(浏览器),在浏览器中会为每个站点(host)创建存储Cookie的空间出来, Cookie的数据存储以Key=Value存储的,但是每个key都有生命周期(有效期)。一个完整的Cookie信息包含: 名称、内容、域名、路径(/)、有效时间(创建时间、到期时间),如查看chrome下localhost域名下的所有cookie数据: chrome://settings/siteData
在搜索Cookie的文本框中输入"localhost", 查出localhost站点之前创建的Cookie。
2.1.1 向客户端写入Cookie
使用response对象的set_cookie()方法可以向客户端添加Cookie:
# werkzeug.wrappers.base_response.BaseResponse
def set_cookie(
self,
key,
value="",
max_age=None,
expires=None,
path="/",
domain=None,
secure=False,
httponly=False,
samesite=None,
):pass
2.1.2 将客户端的Cookie删除
使用响应对象的delete_cookie()方法
def delete_cookie(self, key, path="/", domain=None):
三、模板技术与静态资源
##四、文件上传