Flask开发学习笔记

本文最后更新于:2022年7月21日 下午

Flask开发学习笔记

[TOC]

照着这个学的:

2021年史上最强Flask框架 Flask6天速成从入门到精通(无偿分享附赠课件资料)_哔哩哔哩_bilibili

Flask的g对象和钩子函数 - 知乎 (zhihu.com)

Flask教程(十六)RESTful-API - 迷途小书童的Note迷途小书童的Note (xugaoxiang.com)

Hello world

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 导入Flask库
from flask import Flask
# Flask对象初始化
# 第一个参数指定为:__name__ 表示工程目录
# 可选参数 还可以设置静态文件和模板的文件夹
app = Flask(__name__)

# 配置应用程序
class DefaultSettings:
SECRET_KEY = "ASFG$$#%SDG!GHAxa@%gASD"
DEBUG = True # 开发阶段开启DEBUG模式,部署别开
# 直接给这个类作为参数
app.config.from_object(DefaultSettings)
# 参数也可以从文件中读取
# app.config.from_pyfile('setting.py')

@app.route('/')
def hello_world(): # put application's code here
# 读取配置信息
print(app.config["SECRET_KEY"])
return 'Hello World!'

if __name__ == '__main__':
# run()参数:
# 主机IP地址、端口
app.run()

查询路由

路由就是@app.route('\')这样的路径,不同页面可以分散在不同的文件。

  1. 可以使用命令flask routes快速查询。
  2. app.url_map也包含了所有的路由信息。
  3. app.url_map.iter_rules()也可以用来遍历。

CORS 跨域资源共享

浏览器A从服务器B1B_1中获取了一个网页,这个网页需要显示存放于服务器B2B_2中的广告,这个时候就形成了跨域获取资源,浏览器A出于安全性考虑要对关于进行限制,所以需要使用CORS技术来解决这个问题。解决方法如下:

  1. 浏览器A先向B2B_2发送OPTIONS请求,询问其是否接受来自B1B_1的跨域请求。
  2. B2B_2返回头中包含allow-origin 'www.b1.com',允许跨域访问。
  3. 浏览器A向B2B_2发送正式GET请求。

请求与响应

指定请求方式

在页面路由中可以指定页面的访问方式,若错误方式访问会405

1
@app.route("/test1", methods=["GET", "POST"])

获取URL路径参数

1
2
3
4
5
6
7
8
9
# 使用转换器语法,<>是转换器,要和函数参数同名
# 假如有格式要求:
# <int:user_id> 指定是int类型
# <int(min=1):user_id>
# 也可以使用进阶的自定义转换器
@app.route('/users/<user_id>')
def user_info(user_id):
print(type(user_id), user_id)
return str(user_id)

获取URL请求参数

即获取URL中?后面的参数

1
2
3
4
5
6
# 获取URL参数,需要导入flask.request对象
# /param?id=123
@app.route('/param')
def param():
req_id = request.args.get('id')
return str(req_id)

获取POST请求参数

1
2
3
4
5
6
7
8
9
# 获取POST参数
# 文件通过files来获得流
# 文本参数通过form表单 ImmutableMultiDict([('abc', '1234456')])
@app.route('/upload', methods=['POST'])
def upload():
f = request.files['pic']
with open("./demo.png", 'wb') as new_file:
new_file.write(f.read())
return str(request.form)

响应模板

flask.render_template,估计用不着,先不学。

重定向响应

1
2
3
4
from flask import redirect
@app.route('/redirect')
def redirect_func():
return redirect('http://www.xxx.com')

返回JSON响应

用jsonify返回可以自动设置响应头的格式。

1
2
3
4
5
from flask import jsonify
@app.route('/json')
def json():
some_dict = {}
return jsonify(some_dict)

自定义响应

页面函数返回一个元组(response, status, headers),如("返回文本", 404, {'id_cache': 'asdf31S#$13A'})

也可以使用make_response方式:

1
2
3
4
rsp = make_response('返回文本')
rsp.headers['id_cache'] = "asdf31S#$13A"
rsp.status = "404 not found"
return rsp

Cookies

必须使用make_response方式响应:

  • 设置:rsp.set_cookie('key', 'value', max_age=7200)(age单位秒)
  • 删除:rsp.delete_cookie('key')
  • 获取:rsp.cookies.get('key')

Session

必须先设置SECRET_KEY,导入flask.session

session可以当做字典使用:session['username'] = 'alex'session.get('username')

异常处理

正常向用户抛出异常

需要导入flask.abort,使用方法:abort(状态码)

程序错误捕捉

使用@app.errorhandler

1
2
3
4
5
6
7
8
9
# 假如服务器要返回500错误代码,则会使用下面这个函数的返回。
# 即自定义错误页面
@app.errorhandler(500)
def internal_server_error(e):
return "服务器搬家了"
# 也可以捕获指定代码异常
@app.errorhandler(ZeroDivisionError)
def zero_error(e):
return "0不能做除数"

Flask 蓝图

1
2
3
4
5
6
7
8
9
# 1. 创建蓝图对象 
user_blueprint = Blueprint('user', __name__)
# 2. 蓝图对象上注册路由
@user_blueprint.route("/test1", methods=["GET", "POST"])
def func():
pass
# 3. 应用对象上注册蓝图对象
app.register_blueprint(user_blueprint)
# 之后可以访问网站/user/test1

Flask current_app

在别的地方假如要获取某个app实例的属性(比如redis或者mysql的相关信息在app的配置中,需要在其他文件中获取),不需要导入main.py,只需要导入flask.current_app

假如有多个app实例,那在函数中也用flask.current_app

Flask g对象

g对象是程序全局的一个临时变量,需要导入flask.g。就是给了你一个类似request的变量,在本次请求内各个函数都可以用。(为啥不直接传参?)

Flask 钩子

@app.before_request:每次请求都会执行的函数,无参数,不需要返回值!否则会直接作为返回值而不管页面返回。

@app.before_first_request:在第一次请求之前执行的函数,无参数,不需要返回值。

@app.after_request:每次请求结束后运行,有一个参数用来接收response_class,一般返回这个response。

@app.teardown_request:每次请求结束后运行,比after_request更后,需要一个参数来接收异常(没有则为None)。假如前面有未经处理的异常,就不会调用after_request,但是仍然会调用这个。一般用来释放数据库连接。

graph LR
A[before_first_request] --> B[before_request]
B --> C[page]
C --> D[after_request]
D --> E[teardown_request]
C -.-> E

一个判断登陆实例

1
2
3
4
5
6
7
8
9
10
11
12
def login_required(func):
def wrapper(*args, **kwargs):
if g.user_id is None:
abort(401)
else:
return func(*args, **kwargs)
return wrapper

@app.route("/profile")
@login_required # 先加app.route,再加别的
def profile():
return "hello world."

Flask RESTful

是flask的一个扩展库:pip install flask-restful

下面先来一个实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from flask import Flask, jsonify
from flask_restful import Api, Resource, reqparse

USERS = [
{"name": "zhangsan"},
{"name": "lisi"},
{"name": "wangwu"},
{"name": "zhaoliu"}
]

class Users(Resource): # 继承Resource
def get(self): # 处理GET请求
return jsonify(USERS)
def post(self): # 处理POST请求
# 有点像argparse的reqparse
args = reqparse.RequestParser()
args.add_argument('name', type=str, location='json',
required=True, help="名字不能为空")
args.parse_args()
if args['name'] not in USERS:
USERS.append({"name": args['name']})
return jsonify(USERS)
def delete(self): # 处理DELETE请求
USERS = []
return jsonify(USERS)

app = Flask(__name__) # 先实例Falsk对象
api = Api(app, default_mediatype="application/json") # 再实例RESTful对象

api.add_resource(Users, '/users') # 添加资源到users

app.run(host='0.0.0.0', port=5001, use_reloader=True) # 运行app

稍微修改一下,让GET方法能获取到某个单个用户的信息:

1
2
3
def get(self, userid):
return jsonify({ 'name': USERS[int(userid)].get("name") })
api.add_resource(UserId, '/user/<userid>') # 转换器语法

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!