客户端请求访问某个服务器网站,首先发送 Request 请求,Request 请求中包括请求头、请求体和请求行;
当服务器接收到客户端的 Request 请求后,就会根据请求的内容来返回 Response 对象,其中包括响应头、响应行和响应体,而响应体就是最终展示在客户端浏览器上的网页页面。
flask 之 response 对象
reponse 对象的导入:
from flask import Response
响应对象其实是在路由中函数返回的值,其中函数返回值可以为
- 元组
- 字符串
- 字典
- response 对象
- make_response 对象
视图函数的返回值会被自动转换为一个响应对象,Flask的转换逻辑如下:
- 如果返回的是一个合法的响应对象,则直接返回。
- 如果返回的是一个字符串,那么Flask会重新创建一个
werkzeug.wrappers.Response
对象,Response 将该字符串作为主体,状态码为200,MIME类型为 text/html,然后返回该Response对象。 - 如果返回的是一个元组,元祖中的数据类型是 (response,status,headers) 。status 值会覆盖默认的 200 状态码,headers 可以是一个列表或者字典,作为额外的消息头。
- 如果以上条件都不满足,Flask 会假设返回值是一个合法的 WSGIt 应用程序,并通过 Response.force_type(rv,request.environ) 转换为一个请求对象。
from flask import Flask, Response, make_response
app=Flask(__name__)
# 元组
@app.route('/tuple')
def index1():
return ('元组',200)
# 字典
@app.route('/dict')
def index2():
return {'a':'beijing','b':'shanghai','c':'guangzhou'}
# 字符串
@app.route('/str')
def index3():
return "字符串"
# return后面返回字符串其实是做了一个response对象的封装
# response 实例
@app.route('/response')
def index4():
return Response('response实例') #返回的Response实例
# make_response
@app.route('/make_response')
def index5():
response=make_response('make_response')
return response
if __name__ == '__main__':
app.run()
那么进入相应的路由,返回的结果是:
#元组
{
"a": "beijing",
"b": "shanghai",
"c": "guangzhou"
}
# 字符串
# response实例
# make_response
render_template 方法
当返回内容是一个完整的 HTML 页面的时候,可以通过 render_template()
方法呈现
比如要求返回一个简单的注册页面,首先在 templates 文件中创建名为 register.html 文件,简单编写注册页面的 HTML 代码,然后调用render_template()
方法,并使用此 HTML 文件,即:return render_template('index.html')
这里要特别注意:
render_template()
默认是从 templates 文件夹中获取 HTML 文件,所以我们要把HTML文件放在 templates 中。- 为什么默认是从emplates 文件夹中获取 HTML 文件而不是其他文件夹,其实在实例化 Flask 类 app 的时候,就已经默认了,如上右图。
{#register.html 注册页面 HTML 代码#}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户注册页面</title>
</head>
<body>
<h1>用户注册</h1>
<form action="/register" method="post">
<p><input type="text" name="username" placeholder="用户名"></p>
<p><input type="text" name="password" placeholder="密码"></p>
<p><input type="submit" value="注册"></p>
</form>
</body>
</html>
# app.py
# 省略细节,只展示路由及路由绑定函数
@app.route('/register')
def index():
return render_template('register.html')
本地调试进入对应路由,即可得到下面的结果:
当然你不可否认的是,当然我们也可以创建一个字符串变量,并把 HTML文件内容写在字符串变量中,用路由绑定的视图函数返回(返回类型默认是字符串类型),代码如下:
# app.py
# 通过创建字符串变量返回
@app.route('/register')
def register():
s='''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户注册页面</title>
</head>
<body>
<h1>用户注册</h1>
<form action="/register" method="post">
<p><input type="text" name="username" placeholder="用户名"></p>
<p><input type="text" name="password" placeholder="密码"></p>
<p><input type="submit" value="注册"></p>
</form>
</body>
</html>
'''
return s
显然,这样就太过累赘了,所以一般不会把 HTML 代码写在路由函数中。
make_response 方法
可以使用 make_response 函数来创建 Response 对象,这个方法的特别之处在于可以自定义。可以设置cookie,header(请求头)信息等,比如:
from flask import make_response
@app.route('/make_response')
def test_make_rep():
response = make_response('this is a simple make_response')
# 接下来可以定制响应的相关信息,如响应头、cookie 等等
response.headers['mytest'] = 'tanxy'
return response
flask 之 request 对象
request 请求对象封装了从客户端发来的请求报文信息,我们能从request对象上获取请求报文中的所有数据。 其大部分功能是由依赖包 Werkzeug 完成的,Flask 做了一些特定功能的封装,形成了 request 请求对象。
Flask 框架中的 request 对象保存了一次HTTP请求的一切信息。那么这个HTTP请求中可能会是 GET \ POST 请求,以及还要考虑如何获取各种请求体或者URL参数。
request 常用的属性如下:
属性 | 说明 | 类型 |
---|---|---|
data | 记录请求的数据,并转化为字符串 | * |
form | 记录请求中的表单数据 | MultiDict |
args | 记录请求中的查询参数 | MultiDict |
cookies | 记录请求中的cookie信息 | Dict |
headers | 记录请求中的报文头 | EnvironHeaders |
method | 记录请求中使用的 HTTP 方法 | GET / POST |
url | 记录请求的 URL 地址 | string |
files | 记录请求上传的文件 | * |
- 如果是 json 格式的请求数据,则是采用 request.data 来获取请求体的字符串。
- 如果是 form 表单的请求体,那么则可以使用 request.form 来获取参数。
- 如果是 url 参数,例如:url?param1=xx¶m2=xx,那么则可以使用 request.args 来获取参数。
- 如果需要区分 GET \ POST 请求方法,则可以使用 request.method 来进行判断区分。
- 如果需要接收上传的文件,则可以使用 request.files 来获取上传的文件信息。
GET 请求
写一个接受个人信息的接口,也就是视图函数。我们新建一个名为 register.html 的模板,在模板里写如下表单:
- action:表示要提交到的地址
- method:请求方式
{#register.html#}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<h1 style="color: purple">欢迎来到注册页面</h1>
<form action="/mydata">
<p><input type="text" name="username" placeholder="请输入用户名"></p>
<p><input type="text" name="password" placeholder="请输入密码"></p>
<p><input type="text" name="repassword" placeholder="请重新输入密码"></p>
<p><input type="submit" value="提交"></p>
</form>
</body>
</html>
后台这个时候需要写两个视图,一个视图用于显示注册页面,一个视图用于处理前端传过来的参数:
# app.py
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/') # 首页
def index(): # 视图函数
return render_template('register.html')
@app.route('/mydata') # 代表个人资料信息页面
def center(): # 视图函数
if request.method == 'GET': # 请求方式是get
name = request.args.get('username') # args 取 get 方式参数
password = request.args.get('password')
repassword = request.args.get('repassword')
if password == repassword:
return "注册成功。姓名:%s 密码:%s " % (name, password)
else:
return '请再次注册'
app.config['DEBUG'] = True
if __name__ == '__main__':
app.run() # 运行程序
本地调试结果,进入首页即注册界面,随意输入信息:
点击提交后,可以看到后台已经收到前端参数,并展示到浏览器上,但是有一个问题就是,所有的传递的参数都以字符串形式拼接在了网址上,如下,这样是非常不安全的:
http://127.0.0.1:5005/mydata?username=father&password=123456&repassword=123456
,为什么是这个 URL ?因为我们在HTML 设置了 action 属性,当点击提交时,就会跳转到 http://127.0.0.1:8080/my_data 页面中,通过问号将表单的值拼接起来,其中 username、password、repassword 分别为HTML文件中 input 标签的 name 值。
这也是为什么表单提交方法一般不用 GET 而用 POST 的原因。
POST 请求
谁也不希望自己的一些敏感信息在浏览器的地址上显示。那我们把刚才的列子刚改 POST 请求,前端页面代码只需要把表单里面的 method 改为 post 即可,另外还需在路由装饰器中添加 POST 请求的支持。另外对于后端 POST 请求中,获取参数,应把 args 改成 form
<form action="/mydata" method="post">
<p><input type="text" name="username" placeholder="请输入用户名"></p>
<p><input type="text" name="password" placeholder="请输入密码"></p>
<p><input type="text" name="repassword" placeholder="请重新输入密码"></p>
<p><input type="submit" value="提交"></p>
</form>
```python
# app.py
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/') # 首页
def index(): # 视图函数
return render_template('register.html')
@app.route('/mydata') # 代表个人资料信息页面
def center(): # 视图函数
if request.method == 'POST': # 请求方式是 POST
name = request.form.get('username') # form 取 get 方式参数
password = request.form.get('password')
repassword = request.form.get('repassword')
if password == repassword:
return "注册成功。姓名:%s 密码:%s " % (name, password)
else:
return '请再次注册'
app.config['DEBUG'] = True
if __name__ == '__main__':
app.run() # 运行程序
本地调试:
可以看到我们后台已经接到前端参数,并展示到浏览器上了。并且我们传递的参数不在浏览器地址上拼接了,这样做的好处可以隐藏参数。
其实如果是 POST 请求,我们完全可以不写两个视图函数,可以把两个视图函数合并成一个,通过区分请求的方法实现不同的需求:
- 所谓 GET 请求,大多数是指 “回车键按下的这个请求”,那么就符合我们进入注册界面的这个需求,而提交表单是 POST 请求,一旦点进提交动作,即发送一个 POST 请求,那我们就返回相应的值即可。
- 则此时 HTML 页面代码,form 表单就可以不写 action,代表提交到当前地址上
# app.py final
# 这样更加符合实际开发需求
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/',methods=['GET','POST']) # 代表个人资料信息页面
def center(): # 视图函数
if request.method == 'GET':
return render_template('register1.html')
if request.method == 'POST': # 请求方式是 POST
name = request.form.get('username') # form 取 get 方式参数
password = request.form.get('password')
repassword = request.form.get('repassword')
if password == repassword:
return "注册成功。姓名:%s 密码:%s " % (name, password)
else:
return '请再次注册'
app.config['DEBUG'] = True
if __name__ == '__main__':
app.run() # 运行程序
{#register.html#}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<h1 style="color: purple">欢迎来到注册页面</h1>
<form method="post">
<p><input type="text" name="username" placeholder="请输入用户名"></p>
<p><input type="text" name="password" placeholder="请输入密码"></p>
<p><input type="text" name="repassword" placeholder="请重新输入密码"></p>
<p><input type="submit" value="提交"></p>
</form>
</body>
</html>
这样,在保证代码简洁的同时也增加了可读性,更加符合实际开发。