0%

flask学习笔记(四)

请求与响应 模板[使用方法+源码剖析]

HTTP请求类型
在上一期的最后我们提到了注册路由时的参数,有一个比较重要的参数没有提到:methods,即请求方式,详细介绍methods之前先温习一下HTTP请求方式。
在最新的HTTP/1.1参考意见稿RFC7231(原在RFC2616)中定义了如下几种请求方式。
https://tools.ietf.org/html/rfc7231

请求方式 详细介绍
GET 向特定目标资源发出请求,是信息检索的主要机制。
HEAD HEAD方法与GET类似,不同之处在于服务器在响应中不得发送消息正文,服务器应发送与HEAD请求相同的报头字段。
POST 向指定资源提交数据进行处理请求。数据被包含在请求体中。POST请求可能会导致新的资源的创建和/或已有资源的修改。
PUT 更新指定位置的资源文件。与POST的区别是PUT是幂等的,多次调用同一个PUT请求将始终产生相同的结果。而反复调用POST请求会多次创建相同的资源。
DELETE 请求原始服务器删除目标资源与其当前功能之间的关联。即删除所标识的资源。
CONNECT 请求接收者建立到由请求目标标识的目的源服务器的隧道。
TRACE 回显服务器收到的请求

而在实际应用中主要用到的请求方式有GET POST HEAD PUT DELETE

Flask 请求
注册路由时通过配置methods参数来指定HTTP请求类型。

1
2
3
4
5
6
7
8
9
10
11
from flask import Flask

app = Flask(__name__)

@app.route('/hello', methods=['GET', 'POST']) # 指定GET POST方式均可访问
@app.route('/hello', methods=['POST']) # 指定仅允许POST方式访问,GET方式访问响应为405 Method Not Allowed
def index():
return 'hello world'

if __name__ == '__main__':
app.run()

使用表单以POST方式访问路由
login.html

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<body>

<form action = "http://localhost:5000/login" method = "post">
<p>Enter Name:</p>
<p><input type = "text" name = "username" /></p>
<p><input type = "submit" value = "submit" /></p>
</form>
</body>
</html>

对应后端

1
2
3
4
5
6
@app.route('/login', methods=['POST', 'GET'])
def login():
method = request.method
username = request.form.get('username')
results = '{0}使用{1}方式登录'.format(username, method)
return results

访问login.html,表单中填写信息提交后的结果

这里代码中用到的request对象其实是LocalProxy实例而来,在后边上下文章节中会详细分析LocalProxy,这里先了解一些request包含的属性。
request.form POST请求时的表单,request.form.get(‘username’)获取表单中username的值。
request.args GET请求时的参数,request.args.get(‘username’)获取url中username参数的值。
request.headers 获取请求头。
request.cookies 获取请求的cookies
request.files 获取上传的文件
request.method 获取请求的方法(GET/POST)
request.scheme 获取请求的协议(http)
request.path 获取请求相对路径(/login)
request.url 获取请求的url(http://localhost:5000/login)

Flask 响应
定义路由的视图函数时,return的对象/值 即为响应,之前接触到返回的类型大致有以下三种:

  1. 返回一个字符串 return ‘success’
  2. 返回渲染的模板 return render_template(‘xxx.html’)
  3. 返回重定向路由 return redirect(‘/index’)

除了上述三种情况外,我们也可以自定义response对象。

1
2
3
4
5
6
7
8
9
10
# 自定义返回响应头
new_headers = {'content-type': 'text/html; charset=utf-8', 'secret': 'lockcy is locked'}
res = make_response('自定义内容')
res.headers = new_headers
# 自定义状态码
return res, 666
# 自定义返回数据格式
res = make_response({'name': 'lockcy'})
res.mimetype = 'application/json'
return res # return jsonify({'name': 'lockcy'})

简述了flask中从请求到响应的使用方法,我们再从源码的角度分析下该过程(只给出与本节内容相关的代码,并去除了try except等不影响大体流程的代码)。

1.通过 call 方法将 Flask 对象变为可调用

1
2
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)

2.实现 wsgi_app 函数处理 web 服务器转发的请求

1
2
3
4
5
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ)
ctx.push()
response = self.full_dispatch_request()
return response(environ, start_response)

3.full_dispatch_request 先大喊一声请求来了,并执行3.1 3.2中内容

1
2
3
4
5
request_started.send(self)
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
return self.finalize_request(rv)

3.1. rv = self.dispatch_request(),根据收到的请求到路由与视图函数对应关系中找到符合的,并返回视图函数

1
return self.view_functions[rule.endpoint](**req.view_args)

3.2. return self.finalize_request(rv) 设置响应,再大喊一声请求结束了,并返回响应

1
2
3
4
response = self.make_response(rv)
response = self.process_response(response)
request_finished.send(self, response=response)
return response

模板
在前面的实例中,视图函数的主要作用是生成请求的响应,这是最简单请求。实际上,视图函数有两个作用:
处理业务逻辑
返回响应内容
在大型应用中,把业务逻辑和表现内容放在一起,会增加代码的复杂度和维护成本。
模板其实是一个包含响应文本的文件,其中用占位符(变量)表示动态部分,告诉模板引擎其具体的值需要从使用的数据中获取
使用真实值替换变量,再返回最终得到的字符串,这个过程称为’渲染’
Flask 是使用 Jinja2 这个模板引擎来渲染模板
使用模板的好处
视图函数只负责业务逻辑和数据处理(业务逻辑方面)
而模板则取到视图函数的数据结果进行展示(视图展示方面)
代码结构清晰,耦合度低

在这里不过多关注Jinjia2本身的特性,只关注其与flask有关的功能。
变量取值 { { } }
注释 { # # }
控制结构

1
2
3
4
5
6
7
{% if 'lockcy' in names %}
lockcy
{% elif 'harutya' in names %}
harutya
{% else %}
nonono
{% endif %}

循环结构

1
2
3
4
{% for name in names %}
hello,{{name}}
{% endfor %}

宏定义

1
2
3
4
5
{% macro button(value) %}
<button type='text' style="height:50px;width:100px" onclick="alert('just a test')">{{value}}</button>
{% endmacro %}

<p>{{ button('lockcy') }} </p>

上述代码输出的结果如下:

过滤器
过滤器通过管道符号|与变量链接,并且可以通过圆括号传递参数,如:

1
2
3
4
{# 单词首字母大写 #}
<p>{{ 'hello' | capitalize }}</p>
{# 单词全小写 #}
<p>{{ 'XML' | lower }}</p>

Jinjia2过滤器比较多,详细内容可以参考 https://www.cnblogs.com/liuruitao/p/7787482.html

参考资料
[1]RFC7231:https://tools.ietf.org/html/rfc7231
[2]Jinjia2模板设计者文档:http://docs.jinkan.org/docs/jinja2/templates.html
[3]Jinjia2过滤器:https://www.cnblogs.com/liuruitao/p/7787482.html