FlaskDay03

flask 之 response 对象和 request 对象

目录

客户端请求访问某个服务器网站,首先发送 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')

这里要特别注意:

  1. render_template() 默认是从 templates 文件夹中获取 HTML 文件,所以我们要把HTML文件放在 templates 中。
  2. 为什么默认是从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&param2=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>

这样,在保证代码简洁的同时也增加了可读性,更加符合实际开发。