Flask操作cookie
在 Flask 中操作 Cookie 是相对简单的。Cookie 是一种存储在用户计算机上的小型数据片段,由服务器发送到用户浏览器,然后在每次请求时由浏览器发送回服务器。在 Flask 中,你可以使用 request
对象来读取 cookie,使用 response
对象来设置 cookie。
下面是在 Flask 中进行 Cookie 操作的基本示例:
from flask import Flask, request, make_response
app = Flask(__name__)
@app.route('/')
def index():
# 读取名为 'username' 的 cookie
username = request.cookies.get('username')
return f'Hello {username}'
@app.route('/setcookie/<username>')
def setcookie(username):
# 创建一个 response 对象
resp = make_response('Cookie 设置成功')
# 设置名为 'username' 的 cookie
resp.set_cookie('username', username)
return resp
@app.route('/deletecookie')
def deletecookie():
# 创建一个 response 对象
resp = make_response('Cookie 已删除')
# 删除名为 'username' 的 cookie
resp.delete_cookie('username')
return resp
if __name__ == '__main__':
app.run(debug=True)
在上面的示例中,有三个路由:
/
路由用于读取名为 ‘username
’ 的 cookie,并返回相应的问候信息。/setcookie/<username>
路由用于设置名为 ‘username
’ 的 cookie,其中<username>
是要设置的用户名。/deletecookie
路由用于删除名为 ‘username’ 的 cookie。
设置cookie有效期
- 使用
max_age
参数
在 Flask 中设置 Cookie 的有效期可以在设置 Cookie 时提供 max_age
参数,表示 Cookie 的过期时间 (以秒为单位) 。下面是一个示例,演示了如何在 Flask 中设置带有有效期的 Cookie:
from flask import Flask, make_response
app = Flask(__name__)
@app.route('/setcookie')
def setcookie():
# 创建一个 response 对象
resp = make_response('Cookie 设置成功')
# 设置名为 'username' 的 cookie,并指定有效期为一小时
resp.set_cookie('username', 'John', max_age=3600)
return resp
if __name__ == '__main__':
app.run(debug=True)
在上面的示例中,我们设置了名为 ‘username
’ 的 Cookie,其值为 ‘John’,并且指定了有效期为一小时(3600 秒)。
- 使用
expires
参数
在 Flask 中,你可以使用 expires
参数来设置 Cookie 的到期时间。expires
参数接受一个 datetime
对象,表示 Cookie 的过期时间。下面是一个示例,演示了如何在 Flask 中设置带有到期时间的 Cookie:
from flask import Flask, make_response
from datetime import datetime
app = Flask(__name__)
@app.route('/setcookie')
def setcookie():
# 创建一个 response 对象
resp = make_response('Cookie 设置成功')
# 设置名为 'username' 的 cookie,并指定到期时间为2024年6月1日
expires = datetime(2024, 6, 1)
resp.set_cookie('username', 'John', expires=expires)
return resp
if __name__ == '__main__':
app.run(debug=True)
在上面的示例中,我们设置了名为 ‘username
’ 的 Cookie,其值为 ‘John’,并且指定了到期时间为2024年6月1日。
如果同时使用了
max_age
和expires
两个参数,会以max_age
为准
Flask操作session
在Flask中,Session 是一种在客户端和服务器之间存储信息的机制,可以跟踪用户的会话状态。在 Flask 中操作 Session 分为设置 Session、读取 Session 和删除 Session 三个主要步骤。
Flask设置Session
要在 Flask 中设置 Session,需要使用 Flask 提供的 session
对象。可以像字典一样对其进行操作来设置会话变量。首先,需要安装 Flask-Session 扩展来支持 Session 功能。
from flask import Flask, session
app = Flask(__name__)
app.secret_key = 'your_secret_key' # 设置盐参数(或者叫秘钥)
@app.route('/')
def index():
session['username'] = 'user123'
return 'Session set successfully'
if __name__ == '__main__':
app.run(debug=True)
Flask读取Session
要读取 Session 中的值,只需通过键来访问 session
对象即可。
from flask import Flask, session
app = Flask(__name__)
app.secret_key = 'your_secret_key'
@app.route('/')
def index():
username = session.get('username')
return f'Hello {username}'
if __name__ == '__main__':
app.run(debug=True)
Flask删除Session
要删除 Session 中的值,可以使用 pop()
方法或 clear
关键字。
pop
指定键名clear
清除所有
from flask import Flask, session
app = Flask(__name__)
app.secret_key = 'your_secret_key'
@app.route('/')
def index():
session.pop('username', None) # 删除名为 'username' 的 Session
# 或者使用 del session['username']
return 'Session deleted successfully'
@app.route('/del_all')
def index():
session.clear() # Session中的所有内容
return 'Session deleted successfully'
if __name__ == '__main__':
app.run(debug=True)
这就是在 Flask 中操作 Session 的基本方法。记住,为了保护 Session 数据的安全,务必在 Flask 应用程序中设置一个密钥,这个密钥会被用来签名 Session 数据。
设置session的有效期
可以通过设置 session.permanent
来设置session
有效期,设置为true
之后,实际不是永久,默认保存31天,还可以通过app.config["PERMANENT_SESSION_LIFETIME"]
参数来自定义时间。
from datetime import timedelta
from flask import Flask, session
app = Flask(__name__)
# 设置session的密钥
app.secret_key = 'your_secret_key'
# 设置session有效期为一天
app.config["PERMANENT_SESSION_LIFETIME"] = timedelta(days=1)
@app.route('/')
def index():
# 设置session变量
session.permanent = True # 启用设置有效期
session['user'] = 'example_user'
return 'Session set'
@app.route('/get_session')
def get_session():
# 获取session变量
user = session.get('user', None)
return 'Session user: {}'.format(user)
if __name__ == '__main__':
app.run(debug=True)
使用session实现免登录
使用session实现免登录功能是一种常见的做法,特别是在Web应用中。通过session,你可以在用户登录后存储相关的身份验证信息,然后在用户会话期间保持其登录状态,而无需用户重复登录。
下面是一个简单的示例,演示如何使用session实现免登录功能:
from flask import Flask, session, redirect, url_for, request
app = Flask(__name__)
# 设置session的密钥
app.secret_key = 'your_secret_key'
@app.route('/')
def index():
if 'username' in session:
return 'Logged in as {}'.format(session['username'])
return 'You are not logged in'
@app.route('/login', methods=['POST'])
def login():
if request.method == 'POST':
# 假设这里有一个验证用户的过程
# 如果验证成功,将用户名存储在session中
session['username'] = request.form['username']
return redirect(url_for('index'))
@app.route('/logout')
def logout():
# 退出登录,清除session
session.pop('username', None)
return redirect(url_for('index'))
if __name__ == '__main__':
app.run(debug=True)
在这个示例中,用户在登录后,其用户名将存储在session中。在每次请求中,Flask会检查session中是否存在用户名,如果存在,就会认为用户已经登录。如果用户点击了退出登录的链接,则会清除session中的用户名,用户将被重定向到未登录状态下的页面。
通过这种方式,用户可以在登录一次后,保持其登录状态,直到他们主动选择退出登录为止。
g
对象的使用
在Flask中,g
对象是一个特殊的全局变量,用于在同一请求处理过程中的不同函数之间共享数据。g
对象的作用类似于全局变量,但它只在同一请求期间有效,不同请求之间的g
对象是独立的。g
对象通常用于在请求处理过程中存储临时数据或共享状态。
下面是一些使用g
对象的常见场景和示例:
- 存储临时数据:你可以使用
g
对象在请求处理过程中存储临时数据,以便在不同的函数之间共享。例如,在请求开始时从数据库加载一些数据,并在请求处理过程中多次使用它们。
from flask import Flask, g
app = Flask(__name__)
# 在请求处理前加载一些数据,并存储在g对象中
@app.before_request
def load_data():
g.user = {'username': 'john', 'email': 'john@example.com'}
# 在请求处理中使用g对象中的数据
@app.route('/')
def index():
user = g.user
return f'Hello, {user["username"]}'
if __name__ == '__main__':
app.run(debug=True)
- 共享状态信息:你可以使用
g
对象在请求处理过程中共享状态信息,例如数据库连接、用户身份验证信息等。这样可以避免在每个函数中重复创建和传递这些信息。
from flask import Flask, g
import sqlite3
app = Flask(__name__)
app.config['DATABASE'] = '/path/to/database.db'
# 在请求处理前连接数据库,并存储连接对象在g对象中
def get_db():
if 'db' not in g:
g.db = sqlite3.connect(app.config['DATABASE'])
return g.db
# 在请求处理结束后关闭数据库连接
@app.teardown_appcontext
def close_db(error):
if hasattr(g, 'db'):
g.db.close()
@app.route('/')
def index():
db = get_db()
# 在请求处理中使用g对象中存储的数据库连接
# 这里假设有一些数据库操作
return 'Hello from index'
if __name__ == '__main__':
app.run(debug=True)
通过使用g
对象,你可以在请求处理过程中方便地共享数据和状态信息,而无需将它们传递给每个函数。这样可以简化代码,并使得在同一请求期间的不同函数之间共享数据更加方便。
Flask中信号的使用
Flask中的信号使用是基于第三方模块blinker
的,详情请看我的另一篇博客
Flask中的内置信号
Flask中的内置信号是一种事件通知机制,允许开发者在特定的情况下执行自定义的操作。以下是关于Flask内置信号的介绍:
-
template_rendered
:当模板渲染完成后发送的信号。可以用来执行与模板渲染相关的后处理操作。 -
before_render_template
:在模板渲染之前发送的信号。这允许开发者在模板渲染之前执行一些准备工作或修改模板渲染上下文。 -
request_started
:在请求开始之前发送的信号,即在到达视图函数之前。可以用于执行与请求开始相关的操作,例如记录请求信息或验证请求。 -
request_finished
:在请求处理完成,但在响应发送给客户端之前发送的信号。这个信号可以用于执行与请求结束相关的操作,如日志记录或资源清理。 -
request_tearing_down
:在请求对象被销毁时发送的信号。即使在请求过程中发生异常,也会发送该信号。可以用于执行与请求销毁相关的清理操作。 -
got_request_exception
:在请求过程中抛出异常时发送的信号。异常本身通过exception
参数传递给订阅的函数。通常用于记录网站异常信息或执行异常处理逻辑。 -
appcontext_tearing_down
:在应用上下文被销毁时发送的信号。可以用于执行与应用上下文销毁相关的清理操作。 -
appcontext_pushed
:在应用上下文被推入到栈上时发送的信号。可以用于执行与应用上下文推入相关的初始化操作。 -
appcontext_popped
:在应用上下文被推出栈时发送的信号。可以用于执行与应用上下文推出相关的清理操作。 -
message_flashed
:调用了Flask的flash
方法时发送的信号。可以用于在消息被闪现时执行特定的操作,如将消息记录到日志中。
这些内置信号提供了灵活的扩展点,使开发者能够在Flask应用的不同阶段执行自定义逻辑,从而实现更高级的功能或增强应用的可维护性和可扩展性。
内置信号的使用案例
选择got_request_exception
信号作为案例,这个信号在请求处理过程中抛出异常时发送。一个常见的用例是记录网站异常信息到日志中。下面是一个简单的示例:
from flask import Flask
from flask.signals import got_request_exception
app = Flask(__name__)
def log_exception(sender, exception, **extra):
# 将异常信息记录到日志中
app.logger.error('Exception occurred during request: %s', exception)
# 订阅(got_request_exception)信号,当异常发生时调用log_exception函数
got_request_exception.connect(log_exception, app)
@app.route('/')
def index():
# 引发一个异常,模拟请求处理过程中出现的异常
1 / 0
if __name__ == '__main__':
app.run(debug=True)
在这个例子中,我们创建了一个Flask应用,并定义了一个log_exception
函数来记录异常信息到应用的日志中。然后,我们使用connect()
方法来监听got_request_exception
信号,并将其与log_exception
函数关联起来。当请求处理过程中发生异常时,log_exception
函数会被调用,并将异常信息记录到应用的日志中。
通过这种方式,我们可以方便地跟踪和记录应用中的异常,以便及时发现和解决问题。
WTForms的使用
。WTF(WTForms)是一个强大的Python表单库,常常用来在后端进行数据的校验。
首先,确保你已经安装了Flask-WTF。你可以使用pip进行安装:
pip install Flask-WTF
下面是一个简单的示例,演示如何使用Flask-WTF创建一个简单的登录表单:
from flask import Flask, render_template, request
from wtforms import Form, StringField
from wtforms.validators import Length, EqualTo
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello! '
class RegisterForm(Form):
uname = StringField(validators=[Length(min=2, max=10, message='用户名长度2-10之间')])
pwd = StringField(validators=[Length(min=2, max=10)])
pwd2 = StringField(validators=[Length(min=2, max=10), EqualTo('pwd', message='2次密码不一致')])
@app.route('/register/', methods=['GET', 'POST'])
def register():
if request.method == 'GET':
return render_template('register.html')
else:
form = RegisterForm(request.form)
if form.validate(): # 验证成功:True, 失败:False
return '验证成功!'
else:
return f'验证失败!{form.errors}'
if __name__ == '__main__':
app.run(debug=True)
使用方法
- 创建自定义类,需要继承自
wtforms.Form
- 添加校验字段(
uname
、pwd
、pwd2
),必须和表单的name
值保持一致。 Length
用来校验长度,message
可以设置错误提示,EqualTo
用来判断二者是否一致- 通过
RegisterForm.validate()
判断校验结果 - 通过
RegisterForm.errors
获取错误信息
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>某系统注册页面</title>
</head>
<body>
<form action="/register/" method="post">
<table>
<tr>
<th>用户名:</th>
<td><input type="text" name="uname"></td>
</tr>
<tr>
<th>密码:</th>
<td><input type="password" name="pwd"></td>
</tr>
<tr>
<th>确认密码:</th>
<td><input type="password" name="pwd2"></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="注册"></td>
</tr>
</table>
</form>
</body>
</html>
这是一个简单的注册案例,检验了账号密码长度以及密码的两次密码的一致。
WTForm的常用验证器
在 Flask 中使用 WTForms 验证器进行数据验证时,你可以根据需要选择以下常用验证器:
-
Length: 字符串长度限制,可设置最小值和最大值。
username = StringField(validators=[Length(min=3, max=10, message="用户名长度必须在3到10位之间")])
-
EqualTo: 验证数据是否和另外一个字段相等,常用于密码和确认密码两个字段是否相等。
password_repeat = StringField(validators=[Length(min=6, max=10), EqualTo("password")])
-
Email: 验证上传的数据是否为邮箱数据格式。
email = StringField(validators=[Email()])
-
InputRequired: 验证该项数据为必填项,即要求该项非空。
username = StringField(validators=[input_required()])
-
NumberRange: 数值的区间,可设置最小值和最大值限制,如果处在这两个数字之间则满足。
age = IntegerField(validators=[NumberRange(12, 18)])
-
Regexp: 使用正则表达式进行验证,如验证手机号码。
phone = StringField(validators=[Regexp(r'1[34578]\d{9}')])
-
URL: 必须是 URL 的形式。
home_page = StringField(validators=[URL()])
自定义验证器
你可以按照以下步骤来实现自定义验证器:
- 创建一个基于 WTForms 的自定义验证器类。
- 在类中定义以
validate_字段名(self, field)
命名规则的验证方法。 - 在验证方法中,使用
field.data
获取字段的值,并进行验证。 - 如果验证失败,抛出
wtforms.validators.ValidationError
异常,并传入验证失败的信息。
下面是一个示例代码:
from flask import Flask, request, render_template
from wtforms import Form, IntegerField
from wtforms.validators import InputRequired, ValidationError
app = Flask(__name__)
class MyForm(Form):
custom_field = IntegerField('Custom Field', validators=[InputRequired()])
def validate_custom_field(self, field):
value = field.data
# 示例验证条件:如果字段值小于0,则抛出异常
if value < 0:
raise ValidationError('字段值必须大于等于0')
@app.route('/', methods=['GET', 'POST'])
def index():
form = MyForm(request.form)
if request.method == 'POST' and form.validate():
# 处理表单提交逻辑
return '表单提交成功!'
return render_template('index.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
此处自定义了一个验证validate_custom_field
,校验时自动执行validate_
开头的函数
<!DOCTYPE html>
<html>
<head>
<title>My Form</title>
</head>
<body>
<h2>My Form</h2>
<form method="POST">
{{ form.csrf_token }}
<p>
{{ form.custom_field.label }}<br>
{{ form.custom_field() }}
{% if form.custom_field.errors %}
<span style="color: red;">{{ form.custom_field.errors[0] }}</span>
{% endif %}
</p>
<p><input type="submit" value="Submit"></p>
</form>
</body>
</html>
WTF使用模板渲染
这段代码主要展示了如何使用Flask和WTForms来创建一个简单的表单,并使用WTForms的渲染模板功能来渲染表单到HTML页面上。以下是对代码的整理:
app.py:
from flask import Flask, render_template
from formscheck import CreateForm
app = Flask(__name__)
# 定义路由,用于显示表单页面
@app.route('/createform/')
def createform():
form = CreateForm()
return render_template('create_form.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
formscheck.py:
from wtforms import Form, StringField, IntegerField, BooleanField, SelectField
from wtforms.validators import InputRequired, NumberRange
# 定义表单类
class CreateForm(Form):
uname = StringField("用户名:", validators=[InputRequired()])
age = IntegerField("年龄:", validators=[NumberRange(18, 40)])
remember = BooleanField("记住我:")
addr = SelectField('地址:', choices=[('bj', "北京"), ('sj', '上海'), ('tj', '天津')])
create_form.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.classname {
background: red;
}
</style>
</head>
<body>
<h2>WTForms创建表单</h2>
<form action="#" method="post">
<table>
<tr>
<td>{{ form.uname.label }}</td>
<td>{{ form.uname(class='classname') }}</td>
</tr>
<tr>
<td>{{ form.age.label }}</td>
<td>{{ form.age() }}</td>
</tr>
<tr>
<td>{{ form.remember.label }}</td>
<td>{{ form.remember() }}</td>
</tr>
<tr>
<td>{{ form.addr.label }}</td>
<td>{{ form.addr() }}</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="提交"></td>
</tr>
</table>
</form>
</body>
</html>
自动对验证器类渲染解析并使用在模板中
- 效果
使用WTF进行文件验证
当涉及到文件上传时,验证是非常重要的,可以确保上传的文件符合期望的格式、大小等规范。你可以使用Flask-WTF来简化表单验证的过程。下面是如何使用WTF对文件进行验证:
import os
from flask import Flask, render_template, send_from_directory
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileRequired, FileAllowed
from werkzeug.utils import secure_filename
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'
app.config['UPLOAD_FOLDER'] = 'uploads' # 设置存储路径
class UploadForm(FlaskForm):
file = FileField('File', validators=[
FileRequired(), # 验证文件不能为空
FileAllowed(['txt', 'pdf', 'doc', 'docx'], 'Allowed file types: txt, pdf, doc, docx') # 验证文件后缀名
])
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
form = UploadForm()
if form.validate_on_submit():
file = form.file.data
filename = secure_filename(file.filename)
if not os.path.exists(os.path.join(app.config['UPLOAD_FOLDER'])):
os.mkdir(os.path.join(app.config['UPLOAD_FOLDER']))
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return 'File uploaded successfully'
return render_template('upload.html', form=form)
@app.route('/uploads/<filename>')
def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
if __name__ == '__main__':
app.run(debug=True)
这里我们引入了FlaskForm
,它是WTF表单的基类,用于创建表单。FileField
表示一个文件上传字段,它的validators参数用于添加验证规则。在这个例子中,我们使用FileRequired()
确保文件字段不为空,并使用FileAllowed()
指定允许上传的文件类型。
在HTML模板upload.html
中,你只需要使用form.file
来渲染文件上传字段即可:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Upload</title>
</head>
<body>
<h1>Upload File</h1>
<form method="post" enctype="multipart/form-data">
{{ form.hidden_tag() }}
{{ form.file.label }} {{ form.file() }}
<input type="submit" value="Upload">
{% if form.file.errors %}
<ul>
{% for error in form.file.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</form>
</body>
</html>
这样,你就可以在上传文件时进行验证,并确保上传的文件符合预期的要求。
Flask-RESTful
介绍RESTful
RESTful是一种基于REST架构风格设计的软件架构风格,它是Representational State Transfer(表征状态转移)的缩写。RESTful架构风格在设计网络应用程序时,采用了一系列约束和原则,使得系统更加简单、可扩展、可维护和可理解。下面是RESTful的一些关键特性和原则:
-
资源(Resources):在RESTful架构中,所有的内容都被抽象为资源,每个资源都可以通过唯一的URI来标识。例如,一个博客系统中的文章、评论、用户等都可以是资源。
-
表征(Representation):资源的表现形式可以是多种多样的,比如JSON、XML、HTML等。客户端通过资源的表现形式与服务器进行交互。
-
状态转移(State Transfer):RESTful架构强调通过HTTP协议的各种方法来对资源进行操作,包括GET、POST、PUT、DELETE等,这些方法对应着资源的不同操作。
-
无状态(Stateless):服务器不会保存客户端的状态信息,每个请求都应该包含足够的信息使得服务器可以理解请求。这样的设计使得系统更容易扩展和更加可靠。
-
统一接口(Uniform Interface):RESTful架构提倡使用统一的接口进行通信,使得不同的客户端和服务器可以相互通信。这种设计方式降低了系统的耦合性,提高了系统的可维护性和可扩展性。
通过遵循RESTful架构风格,开发人员可以设计出简单、灵活、高效的网络应用程序,使得不同系统之间的通信更加简单和可靠。
Flask-RESTful的基本使用
Flask-RESTful是一个基于Flask框架的扩展,用于构建RESTful API。它简化了在Flask应用程序中创建RESTful API的过程,提供了一种简单且优雅的方式来定义资源和处理请求。以下是使用Flask-RESTful的一般步骤:
安装Flask-RESTful:首先,你需要安装Flask-RESTful。你可以通过pip来安装它:
pip install flask-restful
基本使用
from flask import Flask, url_for
from flask_restful import Api, Resource
# 创建Flask应用程序
app = Flask(__name__)
# 创建API对象
api = Api(app)
# 创建一个简单的资源类
class HelloWorld(Resource): # 需要继承Resource类
# 定义GET方法处理器
def get(self):
return {'message': '返回用户数据'} # 返回JSON响应
# 定义POST方法处理器
def post(self):
return {'message': '添加新用户返回信息'} # 返回JSON响应
class TestView(Resource):
def get(self):
return {'message': 'test for empty endpoint'}
# 将资源添加到API,并指定URL路径
api.add_resource(HelloWorld, '/user', '/user2', endpoint='user') # 将HelloWorld资源添加到根路径
api.add_resource(TestView, '/test') # 将HelloWorld资源添加到根路径
# 上下文测试
with app.test_request_context():
print(url_for("user")) # 打印/user 当设置多个地址,只会返回第一个地址
print(url_for("testview")) # 打印/test 如果没有设置endpoint,使用类的小写则是默认方案
if __name__ == '__main__':
# 运行Flask应用程序
app.run(debug=True)
-
导入模块:
Flask
用于创建Web应用程序,Api
和Resource
是Flask-RESTful提供的用于构建RESTful API的类。 -
创建
Api
对象,用于管理API的资源。 -
定义资源类
HelloWorld
,继承自Resource
类。在这个类中,定义get
方法作为处理GET请求的处理器,定义post
方法作为处理POST请求的处理器。 -
使用
api.add_resource()
方法将HelloWorld
资源添加到API中,并指定了URL路径为'/'user
,这里可以指定多个路径,都可以使用这个类的逻辑。
还可以设置
endpoint
参数,用来作为标识,当没有设置的时候,默认标识是类名的全小写,当使用url_for
的时候,如果设置了多个路径,只会返回第一个路径。
使用 reqparse
解析数据
在 Flask-RESTful 中,reqparse
是一个用于解析和验证请求参数的实用工具。它可以简化处理客户端发送的数据,确保数据的有效性和完整性。
reqparse
允许我们定义期望的请求参数,并提供类型验证和错误处理。下面是一个示例:
from flask import Flask
from flask_restful import Api, Resource, reqparse
# 创建一个基本的 Flask 应用并添加 Flask-RESTful 支持
app = Flask(__name__)
api = Api(app)
# 定义资源
class User(Resource):
def post(self):
parser = reqparse.RequestParser()
# 定义期望的参数
parser.add_argument('name', type=str, required=True, help='Name cannot be blank!')
parser.add_argument('age', type=int, help='Age of the user')
parser.add_argument('gender', type=str, choices=('male', 'female'), help='Gender must be either "male" or "female"')
# 解析参数
args = parser.parse_args()
return {'message': f"Hello, {args['name']}! You are {args.get('age', 'of unknown age')} years old and your gender is {args.get('gender', 'unspecified')}."}, 201
# 添加资源到API
api.add_resource(User, '/user')
if __name__ == '__main__':
app.run(debug=True)
在这个示例中,我们定义了一个 User
资源,并在 post
方法中使用 reqparse
来解析请求体中的 name
、age
和 gender
参数。
参数验证
reqparse
可以对参数进行验证和处理。例如,可以设置参数类型、默认值、必需性等:
parser.add_argument('name', type=str, required=True, help='Name cannot be blank!')
parser.add_argument('age', type=int, help='Age of the user', default=18)
parser.add_argument('gender', type=str, choices=('male', 'female'), location='form', help='Gender must be either "male" or "female"')
- type:参数的数据类型。如果类型不匹配,将返回错误。
- required:是否为必需参数。如果未提供该参数,将返回错误。
- help:当验证失败时返回的错误信息。
- choices:参数的可选值列表。如果参数的值不在列表中,将返回错误。
- location:指定从哪里获取参数(例如:
json
、args
、form
)。
parser.add_argument
参数介绍
参数 | 类型 | 默认值 | 说明 | 示例 |
---|---|---|---|---|
name | str | 无 | 参数名称 | parser.add_argument('name') |
type | callable | str | 参数的数据类型,传入一个可调用对象(如 int , str , float )用于类型转换 | parser.add_argument('age', type=int) |
required | bool | False | 是否为必需参数,如果未提供则返回错误 | parser.add_argument('name', required=True) |
help | str | 无 | 当参数验证失败时返回的错误信息 | parser.add_argument('name', required=True, help='Name cannot be blank!') |
default | 任意类型 | 无 | 参数的默认值,如果请求中未提供则使用此值 | parser.add_argument('age', type=int, default=18) |
choices | list | 无 | 参数的可选值列表,如果值不在列表中则返回错误 | parser.add_argument('gender', type=str, choices=['male', 'female']) |
location | str 或 list | 无 | 指定从请求的哪个部分获取参数,选项包括 'json' , 'args' , 'form' , 'headers' , 'cookies' , 'files' | parser.add_argument('name', location='json') |
action | str | 'store' | 指定对参数值执行的操作,如 'store' , 'store_const' , 'append' , 'append_const' , 'count' | parser.add_argument('tags', action='append') |
dest | str | 参数名称 | 参数存储在 args 对象中的属性名称 | parser.add_argument('username', dest='user_name') |
trim | bool | False | 是否自动去除参数值两端的空格 | parser.add_argument('name', type=str, trim=True) |
case_sensitive | bool | True | 是否区分参数值的大小写 | parser.add_argument('gender', type=str, choices=['male', 'female'], case_sensitive=False) |
完整示例
为了更好地理解上述参数的用法,以下是一个综合示例,展示了如何使用 reqparse
来解析和验证请求参数:
from flask import Flask
from flask_restful import Api, Resource, reqparse
app = Flask(__name__)
api = Api(app)
class User(Resource):
def post(self):
parser = reqparse.RequestParser()
parser.add_argument('name', type=str, required=True, help='Name cannot be blank!', location='form', trim=True)
parser.add_argument('age', type=int, help='Age of the user', default=18, location='form')
parser.add_argument('gender', type=str, choices=['male', 'female'], help='Gender must be either "male" or "female"', location='form', case_sensitive=False)
parser.add_argument('tags', action='append', help='Tags for the user', location='form')
args = parser.parse_args()
return {
'message': f"Hello, {args['name']}! You are {args.get('age', 'of unknown age')} years old, your gender is {args.get('gender', 'unspecified')}, and your tags are {args.get('tags', [])}."
}, 201
# 添加资源到API
api.add_resource(User, '/user')
if __name__ == '__main__':
app.run(debug=True)
数据样例
下面是一些使用 curl
命令和 JSON 数据格式的示例请求,这些示例演示了如何向 /user
端点发送数据,以便触发 User
资源的 post
方法。
示例 1:所有参数都提供且正确
curl -X POST http://127.0.0.1:5000/user -H "Content-Type: application/x-www-form-urlencoded" -d "name=John&age=30&gender=male&tags=developer&tags=blogger"
预期响应:
{
"message": "Hello, John! You are 30 years old, your gender is male, and your tags are ['developer', 'blogger']."
}
示例 2:缺少可选参数
age
和tags
curl -X POST http://127.0.0.1:5000/user -H "Content-Type: application/x-www-form-urlencoded" -d "name=Jane&gender=female"
预期响应:
{
"message": "Hello, Jane! You are 18 years old, your gender is female, and your tags are []."
}
示例 3:缺少必需参数
name
curl -X POST http://127.0.0.1:5000/user -H "Content-Type: application/x-www-form-urlencoded" -d "age=25&gender=male"
预期响应:
{
"message": {
"name": "Name cannot be blank!"
}
}
示例 4:参数
gender
的值不在指定选项中
curl -X POST http://127.0.0.1:5000/user -H "Content-Type: application/x-www-form-urlencoded" -d "name=Alex&age=40&gender=other"
预期响应:
{
"message": {
"gender": "Gender must be either \"male\" or \"female\""
}
}
示例 5:发送 JSON 格式的数据
curl -X POST http://127.0.0.1:5000/user -H "Content-Type: application/json" -d '{"name": "Emma", "age": 22, "gender": "female", "tags": ["artist", "musician"]}'
预期响应:
{
"message": "Hello, Emma! You are 22 years old, your gender is female, and your tags are ['artist', 'musician']."
}
示例 6:参数值含有空格,启用
trim
功能
curl -X POST http://127.0.0.1:5000/user -H "Content-Type: application/x-www-form-urlencoded" -d "name= Alice &age=27&gender=female"
预期响应:
{
"message": "Hello, Alice! You are 27 years old, your gender is female, and your tags are []."
}
使用 marshal_with
规范化 返回值
它允许你定义一个响应模板,将返回的对象映射到这个模板上,确保返回的数据结构符合你的预期。
创建一个简单的 Flask 应用并使用 Flask-RESTful 创建 API 接口:
from flask import Flask, request
from flask_restful import Api, Resource, fields, marshal_with
app = Flask(__name__)
api = Api(app)
user_fields = {
'code': fields.Integer(default=200),
'message': fields.String
}
class UserResponse:
def __init__(self, message):
self.message = message
class UserResource(Resource):
@marshal_with(user_fields)
def post(self):
user_response = UserResponse(message=request.values.get('message'))
return user_response
api.add_resource(UserResource, '/user')
if __name__ == '__main__':
app.run(debug=True)
在这个案例中,当给http://127.0.0.1:5000/user
发送数据之后,即使没有给返回对象添加code
的值,使用规范化参数之后,其自动添加然后返回数据。
fields
参数设置
Flask-RESTful 是一个扩展 Flask 的库,它能够方便地构建基于 REST 的 API。这个库简化了开发 RESTful 服务所需的一些常见任务,比如 URL 路由和请求处理。fields
模块在 Flask-RESTful 中用于定义资源的序列化规则,即如何将 Python 对象转换为 JSON 格式进行响应。
fields
模块
fields
模块提供了一系列字段类型和辅助工具,帮助你指定如何序列化你的资源字段。常用的字段类型包括:
fields.String
fields.Integer
fields.Float
fields.Boolean
fields.List
fields.Nested
fields
参数详解
下面是一些常见的 fields
参数及其用法。
fields.String
用于表示字符串类型的字段。
from flask_restful import fields
resource_fields = {
'name': fields.String,
'description': fields.String(default='No description')
}
参数
attribute
: 指定对象中的哪个属性用于序列化。如果不提供,默认使用字段名。default
: 如果对象中没有该字段,使用默认值。
fields.Integer
用于表示整数类型的字段。
resource_fields = {
'id': fields.Integer,
'age': fields.Integer(default=0)
}
参数
attribute
: 指定对象中的哪个属性用于序列化。如果不提供,默认使用字段名。default
: 如果对象中没有该字段,使用默认值。
fields.Float
用于表示浮点数类型的字段。
resource_fields = {
'price': fields.Float,
'rating': fields.Float(default=0.0)
}
参数
attribute
: 指定对象中的哪个属性用于序列化。如果不提供,默认使用字段名。default
: 如果对象中没有该字段,使用默认值。
fields.Boolean
用于表示布尔类型的字段。
resource_fields = {
'is_active': fields.Boolean,
'is_verified': fields.Boolean(default=False)
}
参数
attribute
: 指定对象中的哪个属性用于序列化。如果不提供,默认使用字段名。default
: 如果对象中没有该字段,使用默认值。
fields.List
用于表示一个列表。需要指定列表中的元素类型。
resource_fields = {
'tags': fields.List(fields.String)
}
参数
cls_or_instance
: 列表中元素的字段类型。
fields.Nested
用于表示嵌套的复杂对象。
nested_fields = {
'street': fields.String,
'city': fields.String,
'state': fields.String,
'zipcode': fields.String
}
resource_fields = {
'name': fields.String,
'address': fields.Nested(nested_fields)
}
参数
model
: 嵌套对象的字段定义。allow_null
: 是否允许嵌套对象为空。
使用示例
定义一个资源类并使用 marshal_with
装饰器应用字段规则:
from flask import Flask
from flask_restful import Api, Resource, fields, marshal_with
app = Flask(__name__)
api = Api(app)
resource_fields = {
'id': fields.Integer,
'name': fields.String,
'age': fields.Integer,
'address': fields.Nested({
'street': fields.String,
'city': fields.String,
'state': fields.String,
'zipcode': fields.String
})
}
class UserResource(Resource):
@marshal_with(resource_fields)
def get(self):
user = {
'id': 1,
'name': 'John Doe',
'age': 30,
'address': {
'street': '123 Elm St',
'city': 'Springfield',
'state': 'IL',
'zipcode': '62701'
}
}
return user
api.add_resource(UserResource, '/user')
if __name__ == '__main__':
app.run(debug=True)
在这个示例中,marshal_with
装饰器根据 resource_fields
的定义将 UserResource
的返回值序列化为 JSON 格式。
RESTful
和蓝图结合使用
在 Flask 中,RESTful 和蓝图(Blueprints)的结合使用能够使应用程序的结构更加清晰和模块化。
from flask import Flask, Blueprint
from flask_restful import Api, Resource, fields, marshal_with
# 创建 Flask 应用
app = Flask(__name__)
# 创建蓝图
api_bp = Blueprint('api', __name__)
api = Api(api_bp)
# 定义资源字段
resource_fields = {
'id': fields.Integer,
'name': fields.String,
'age': fields.Integer,
}
# 定义资源类
class UserResource(Resource):
@marshal_with(resource_fields)
def get(self, user_id):
user = {
'id': user_id,
'name': 'John Doe',
'age': 30
}
return user
# 将资源添加到蓝图的 API 中
api.add_resource(UserResource, '/user/<int:user_id>')
# 注册蓝图
app.register_blueprint(api_bp, url_prefix='/api')
# 运行应用
if __name__ == '__main__':
app.run(debug=True)
创建蓝图和 API:
Blueprint('api', __name__)
创建一个名为api
的蓝图。Api(api_bp)
将蓝图传递给 Flask-RESTful 的Api
对象,使其能够处理蓝图中的路由。
定义资源和字段:
- 使用
fields
模块定义资源的序列化规则。 - 创建一个继承自
Resource
的资源类UserResource
,并使用marshal_with
装饰器将返回的 Python 对象序列化为 JSON。
添加资源到 API:
api.add_resource(UserResource, '/user/<int:user_id>')
将UserResource
添加到 API 中,指定它的访问路径。
注册蓝图:
app.register_blueprint(api_bp, url_prefix='/api')
将蓝图注册到 Flask 应用,并设置 URL 前缀为/api
,使得所有蓝图中的路由都以/api
开头。
和RESTful的基本使用相比,其实就是将api对象的创建进行修改,把原本的app对象换成蓝图对象
api = Api(app) # 基本
api = Api(api_bp) # 使用蓝图