Flask是一个轻量级的web开发框架,依赖jinja2和Werkzeug WSGI服务的一个微型框架。
官方文档:https://flask.palletsprojects.com/en/2.0.x/
中文文档:http://docs.jinkan.org/docs/flask/
中文文档的版本会比较低,如果英语OK的话,可以看官方文档。
安装&入门例子
使用pip命令安装:
pip install flask
新建一个py脚本,这里是flask_hello.py脚本,脚本内容如下:
from flask import Flask
app = Flask(__name__)@app.route("/")defhello_world():return"<h1>Hello World!</h1>"
脚本中,需要导入Flask类,并创建Flask对象app,然后使用app.route()装饰器设置hello_world()的路由(即接口路径)。
脚本编辑好之后,启动该脚本的flask服务,切换到脚本所在目录,执行flask run命令:
flask --app flask_hello run
(flask_hello为脚本名称)
启动flask服务的命令,不同的flask版本不太一样,我这里是2.2.2版本的,如果想知道其他的版本的,可以去官网选择对应版本查看
启动flask服务还有另一个方法,就是在脚本里写上启动代码:
from flask import Flask
app = Flask(__name__)@app.route("/")defhello_world():return"<h1>Hello World!</h1>--python命令启动"if __name__=="__main__":# 启动flask服务
app.run()
python命令执行该脚本,结果如下:
路由
我们访问http或https链接(即url)的时候,是有接口路径,在flask服务中,可以根据接口路径确定是哪个程序处理该请求。路由有静态路由和动态路由。设置路由的方式是先定义一个Flask对象,然后根据Flask对象中的
route(rule: str, **options: t.Any)
定义路由
静态路由
静态路由即路径是固定的,没有变动的,如入门的例子中,
@app.route("/")
就是静态的根路由。
示例如下:
py脚本内容定义如下:
from flask import Flask
app = Flask(__name__)@app.route("/search/video")defsearch_videos():return"查找视频资源"@app.route("/search/picture")defsearch_pictures():return"查找图片资源"if __name__=="__main__":
app.run()
其中,
/search/video
和
/search/picture
是定义的静态路由,访问时的接口路径无变动
动态路由
有时候,接口路径是变动的,比如查询用户信息,查询用户A、用户B、用户C,并且用户信息不可控,不能每一个用户都造个路由,flask提供了动态路由,将变动的信息定义为一个变量。
添加路由时,将变动的部分使用
<变量名>
定义,然后被装饰的函数或方法,参数的名称与定义的变量名一致,这样函数或方法就可以使用该变量及其值。
示例:
from flask import Flask
app = Flask(__name__)@app.route("/userinfo/<user_id>/info")defsearch_videos(user_id):returnf"这个是{user_id}的用户信息"if __name__=="__main__":
app.run()
执行脚本,启动flask服务后,访问如下:
从结果中可以看出,/userinfo/后/info前的内容可以变动,也能处理成功。
限定类型
动态路由中,可以给变量限制其数据类型,格式:
<类型:变量名>
数据类型有如下:
类型描述string(缺省值)接受任何不包含斜杠的文本int接受正整数float接受正浮点数path类似string,但可以包含斜杠uuid接受UUID字符串
示例:
from flask import Flask
app = Flask(__name__)@app.route("/userinfo/<int:user_id>/info")defsearch_videos(user_id):returnf"用户编号{user_id}的信息如下:巴拉巴拉巴拉。。。"if __name__=="__main__":
app.run()
脚本执行后,flask服务启动,访问如下:
在动态路由中的变量的数据类型限制比较简单,如果是复杂的校验,则可以在函数内部进行校验。
地址尾部的
/
有时候我们在接口路径末尾加
/
和不加
/
的时候,访问是一致的,但有些加了
/
访问又报错,flask对路由尾部的
/
是有做处理的:
- flask添加路由的尾部带有
/
:访问时加/
和不加/
效果是一样的 - flask添加路由的尾部没有
/
:访问时加/
访问会报错,未加/
才能访问成功
示例如下:
from flask import Flask
app = Flask(__name__)@app.route("/add1/")defadd1():returnf"路由的末尾有'/'"@app.route("/add2")defadd2():returnf"路由的末尾没有'/'"if __name__=="__main__":
app.run()
启动flask服务后,执行结果如下:
请求方式设置
请求方式有GET、POST、PUT等等,常用的有GET和POST,我们可以在Flask()对象设置路由时传入
methods
参数来设置路由的请求方式,格式:
@Flask().route(rule, methods=["GET", xxx, xxx])
脚本内容如下:
from flask import Flask
app = Flask(__name__)@app.route("/get_demo/info", methods=["GET"])defonly_get():return"只支持get请求方式"@app.route("/post_and_get_demo/info", methods=["GET","POST"])defboth_post_get():return"支持get和post的请求方式"if __name__=="__main__":
app.run()
启动对应的flask服务后,接口访问如下:
请求数据处理
发送请求的时候,必定会接触到上传的参数,然后根据上传的参数进行逻辑处理。那我们怎么获取请求上传的数据呢?flask框架提供了request类,可以通过该类获取对应的请求数据。
属性/方法说明args记录了请求中的查询参数,返回类似于字典的数据json记录了请求中的json数据(dict类型)files记录了请求上传的文件,返回类似于字典的数据form记录了请求中的表单数据,返回类似于字典的数据method记录了请求使用的HTTP方法url记录了请求中的URL地址host记录了请求的域名headers记录了请求的请求头信息,返回类似于字典的数据
示例:
from flask import Flask, request
wen = Flask(__name__)@wen.route("/request/info", methods=["GET"])defget_request_info():print(f"request.args类型:{type(request.args)}")print(f"request.args = {request.args}\n")print(f"request.json类型:{type(request.json)}")print(f"request.json = {request.json}\n")print(f"request.files类型:{type(request.files)}")print(f"request.files = {request.files}\n")print(f"request.form类型:{type(request.form)}")print(f"request.form = {request.form}\n")print(f"request.method类型:{type(request.method)}")print(f"request.method = {request.method}\n")print(f"request.url类型:{type(request.url)}")print(f"request.url = {request.url}\n")print(f"request.host类型:{type(request.host)}")print(f"request.host = {request.host}\n")print(f"request.headers类型:{type(request.headers)}")print(f"request.headers = {request.headers}\n")return{"status":0,"message":"get success"}if __name__ =="__main__":
wen.run()
执行python脚本(即启动flask服务),发送请求
控制台输出如下:
请求参数处理
url链接中,有时会有请求参数,我们可以通过flask.request下的args属性获取到请求参数
示例:
from flask import Flask, request
wen = Flask(__name__)@wen.route("/get/info", methods=["GET","POST"])defget_request_info():
data ={}
data.update(request.args)print(data)return data
if __name__ =="__main__":
wen.run()
启动flask服务后,发送请求,查看响应报文:
请求体json数据处理
发送请求时,有时请求体数据是json数据,flask可以通过flask.request下的json属性获取到json请求体数据
脚本内容如下:
from flask import Flask, request
app = Flask(__name__)@app.route("/post/info", methods=["POST"])defpost_json_data():print(f"请求json报文:{request.json}")return{"code":0,"body_data": request.json}if __name__ =="__main__":
app.run()
启动flask服务后,发送post请求,结果如下:
表单请求数据处理
发送请求时,有时请求体数据是json数据,flask可以通过flask.request下的form属性获取到form表单数据
脚本内容如下:
from flask import Flask, request
app = Flask(__name__)@app.route("/form/info", methods=["POST"])defpost_form_data():print(f"form数据:{request.form}")return{"code":0,"form_data": request.form}if __name__ =="__main__":
app.run()
执行脚本,启动flask服务后,发送请求,相关结果如下:
文件请求数据处理
有些请求是上传文件,flask可以通过flask.request下的files属性获取到上传的文件数据。
request.files
:返回类似一个字典类型的数据,包含了所有上传的文件对象FileStorage,可以通过get(“xxx”)获取到具体的文件对象,如request.files.get("data")
表示获取参数是data的文件对象
文件对象FileStorage有多个方法支持操作,常用的有:
属性/方法说明filename文件名称stream文件流对象save(dst, buffer_size=16384)将文件对象的内容保存到指定路径dst
脚本内容如下:
from flask import Flask, request
app = Flask(__name__)@app.route("/form/info", methods=["POST"])defpost_form_data():print(f"files数据:{request.files}")
fo = request.files.get("data")for data in fo.stream:print(data.decode("utf-8"), end="")return{"code":0,"file_name": fo.filename}if __name__ =="__main__":
app.run()
执行脚本(启动flask服务),发送文件上传的请求
控制台输出内容为:
响应数据设置
返回的响应报文有多种格式,比如文本、json、html等。
返回文本
响应报文如果是文本内容时,直接在函数内return字符串内容,flask会封装成文本格式的响应报文
示例:
from flask import Flask
app = Flask(__name__)@app.route("/text", methods=["POST"])deftext_resp():return"响应报文内容是文本内容"if __name__ =="__main__":
app.run()
执行脚本后,postman发送请求如下:
从响应报文中可以看到响应头的Content-Type是text/html,响应报文体是个文本内容
设置状态码和请求头
除了设置响应报文体外,我们还可以返回元组,元组必须包含报文体,可以包含响应状态码和响应头,返回的元组设置如下:
- (response, status)
- (response, headers)
- (response, status, headers)
响应状态码status默认为200,headers会根据response进行简单的调整。
示例:
from flask import Flask
app = Flask(__name__)@app.route("/demo1", methods=["POST"])defresponse_status():return"响应报文内容是文本内容",[email protected]("/demo2", methods=["POST"])defresp_json_status():return{"name":"wenxiaoba","age":18},[email protected]("/demo3", methods=["POST"])defresp_headers():return"设置了响应报文和响应头",{"token":"123456abcde","address":"where are you"}@app.route("/demo4", methods=["POST"])defresp_status_headers():return"设置了响应报文和响应头",202,{"token":"123456abcde","address":"where are you"}if __name__ =="__main__":
app.run()
执行脚本(启动flask服务),发送请求,相关内容如下:
请求/demo1时,可以看到响应报文体和响应状态码是设置的内容
请求/demo2时,响应报文体是json格式的
请求/demo3时,返回了设置的响应报文体,响应状态码是flask默认的200,响应头也有包含设置的字段
请求/demo4时,返回的响应报文体、状态码均为设置的,响应头也包含了设置的字段
返回json
大多数接口返回的响应数据是json格式,flask框架中,有2种方式返回json格式的响应报文:
- 直接返回dict:flask底层会将dict转成json格式
- 使用jsonify()方法,通过参数传入键值对或字典返回json数据,需要导入flask.jsonify
返回dict(即字典)数据时,客户端实际接收到的是json数据
/jsonify1接口是通过jsonify()传入字典参数处理的响应报文
/jsonify2接口是通过jsonify()传入键值对参数处理的响应报文
返回其他格式
除了经常用的json和文本格式外,还会返回其他格式,比如html、xml等,我们可以使用render_template()函数进行处理。
使用render_template()函数需要先导入flask.render_template,函数如下:
render_template(template_name_or_list, **context)
- template_name_or_list:一般传入模板的名称,模板必须存放在与脚本同级的
templates
目录下 - context:上下文,一般模板里面有些要替换的变量(格式是:
{{变量名}}
),可以通过这个参数传入,传入键值对,如:变量名=值
py脚本内容如下:
from flask import Flask, render_template
app = Flask(__name__)@app.route("/gethtml", methods=["POST"])defget_html():return render_template('demo.html', name="wenxiaoba")@app.route("/getxml", methods=["POST"])defget_xml():# 由于返回的是xml数据,所以需要设置响应头的content-type内容,说明传输的是xml数据,否则会被当成html数据return render_template('data.xml', book_name="随便起的书名"),{"content-type":"application/xml"}if __name__ =="__main__":
app.run()
在脚本同一目录下,创建templates目录,在templates目录下创建demo.html、data.xml文件。
demo.html的内容如下:
<html><body><h1>{{name}}的博客</h1><p>这是一个非常勤奋的娃,她想做测试开发,赚更多的钱</p></body></html>
data.xml的内容如下:
<book><body><book_name>python简明教程</book_name><author>谁谁谁</author><price>56</price></body><body><book_name>{{book_name}}</book_name><author>不知道</author><price>62.3</price></body></book>
执行py脚本(启动flask服务),发送请求,结果如下:
设置额外数据——make_response()
如果想设置更多的响应信息,比如cookie,可以通过
make_response()
获取一个响应对象,可以在该对象中设置cookie、请求头等内容。
示例:
py脚本内容如下:
from flask import Flask, request, make_response, render_template
app = Flask(__name__)@app.route("/setcookie", methods=["POST"])defset_cookie():type= request.json.get("type")if"html"==type:# 如果请求上传的json中,type为html时,返回html内容
resp = make_response(render_template("demo.html", name="wenxiaoba"))else:
dict_data ={"name":"wenxiaoba","age":32,"gender":True}
resp = make_response(dict_data)# 设置cookie,
resp.set_cookie("cookie1","cookie1_value")
resp.set_cookie("cookie2","cookie2 value")# 设置响应头
resp.headers["test"]="test headers value"return resp
if __name__ =="__main__":
app.run()
在脚本执行(即启动flask服务)后,发送请求,结果如下:
启动服务配置
在前面例子中,我们基本上是通过脚本执行run()方法来启动flask服务,run()方法可以传入多个参数,对服务内容进行配置,但是我们均未设置参数,现在来说下比较常用的参数。
run()方法:
run(host, port, debug, load_dotenv=True, **options)
- host:默认启动127.0.0.1,这个只能本机访问,0.0.0.0只能局域网访问,如果想要公网正常访问,则将flask服务部署到公网服务器,并将服务器ip作为host参数传入
- port:监听端口号,默认为5000,int数据类型,可以传入其他端口号
- debug:是否开启debug模式,默认为False(即production),如果为True,则会监听脚本是否被保存,如果有保存操作,则按照最新保存的脚本重新启动flask服务
示例:
py脚本内容为:
from flask import Flask
app = Flask(__name__)@app.route("/demo", methods=["POST"])defdemo():return"content data text"if __name__ =="__main__":
app.run(host="192.168.1.105", port=8888, debug=True)
执行脚本(即启动flask服务)后,从控制台日志可以看到,现在的flask服务在192.168.1.105的8888端口进行监听(当然flask服务也部署在192.168.1.105设备上),而且也提示了Debugger模式在生效中
我们发送请求到8888端口
当我们对py脚本进行修改,并保存,保存的时候可以看到控制台重新加载了flask服务,这就是debug模式的优势,脚本编辑了之后,不用手动重新启动flask,只需要保存就会重新加载flask服务。
RESTFul风格规范
flask-restx插件简单使用
一般接口使用的规范是RESTFul风格规范(我也不太清楚),flask-restx是一个支持RESTFul的flask插件,用于规范化接口的编写,并且支持swagger文档
官方说明:https://github.com/python-restx/flask-restx
官方文档:https://flask-restx.readthedocs.io/en/latest/
安装:
pip install flask-restx
简单入门
我们按照官方的示例来入门,官方脚本如下:
from flask import Flask
from flask_restx import Resource, Api
# 创建Flask对象app
app = Flask(__name__)# 创建Api对象api,创建时,将Flask对象app作为参数传入
api = Api(app)# 使用Api对象api来添加路由(不使用Flask对象app来添加路由)# 至于methods,不在添加路由时限制,在装饰的类方法中限制@api.route('/hello')classHelloWorld(Resource):# 创建HelloWorld类,必须继承Resource模块defget(self):# 定义RESTFul风格的get方法(对应get请求方式)return{'hello':'world'}if __name__ =='__main__':
app.run(debug=True)
执行脚本(启动flask服务),发送请求,结果如下:
根据示例中的注释,如果我们想定义一个接口,支持get和post的请求方式,则需要在对应的类中定义get()和post()方法,简单示例如下:
py脚本内容如下:
from flask import Flask, request
from flask_restx import Resource, Api
app = Flask(__name__)
api = Api(app)@api.route("/person/info")classPerson(Resource):defget(self):return"get方法不安全,没有权限查看个人信息"defpost(self):
name = request.json.get("name")return{"name": name,"age":27,"gender":True}if __name__=="__main__":
app.run()
执行py脚本(启动flask服务)后,发送请求,结果如下:
get请求:
post请求:
如果请求方式非get或post,则报错
添加路由
从 简单入门 的示例中,我们知道了怎么添加路由,其实flask_restx插件还提供了add_resource()方法来添加路由。即flask_restx插件有2种方法来添加路由:
- route()装饰器添加路由
- add_resource()方法添加路由
route()装饰器添加路由
route()可以一次性添加多个路由,示例如下:
py脚本内容如下:
from flask import Flask, request
from flask_restx import Resource, Api
app = Flask(__name__)
api = Api(app)@api.route("/path1/demo","/path2/demo","/path3/demo")classdemo(Resource):defpost(self):returnf"接口路径是:{request.path}"if __name__=="__main__":
app.run()
执行脚本(启动flask服务),发送请求,结果如下:
add_resource()方法添加路由
add_resource()是给指定的类添加路由,示例如下:
py脚本内容如下:
from flask import Flask, request
from flask_restx import Resource, Api
app = Flask(__name__)
api = Api(app)classDemo(Resource):defpost(self, name):return{"path": request.path,"name": name}# 给Demo类添加路由
api.add_resource(Demo,'/demo1/<name>','/demo2/<name>/info')if __name__=="__main__":
app.run()
执行脚本(启动flask服务),发送请求,结果如下:
编写RESTFul风格的接口
在设计框架的时候,一般遵循
复用性、高内聚、低耦合
,即容易维护,减少冗余。
高耦合:可以理解为程序非常复杂难以维护,若修改了程序的某一处内容,涉及到方方面面(其他功能)都要跟着改,比如一个程序有100个函数执行正常,然而修改了其中1个函数,其他99个函数都要跟着修改,就是高耦合的场景。低耦合要求将完整的流程拆分成几个独立的模块、独立的功能,模块内的修改不影响模块之间的交互逻辑,比如商户订单管理和用户订单支付都是不同的模块,修改了商户订单管理的某个功能后,用户订单支付的大部分功能不需要修改。
高耦合示例
from flask import Flask, request
app = Flask(__name__)@app.route("/demo", methods=["GET","POST","PUT","DELETE"])defdemo():if request.method =="GET":return"获取订单"elif request.method =="POST":return"生成订单"elif request.method =="PUT":return"修改订单"else:return"删除订单"
该示例中,将订单的增删改查都在一个函数里完成,如果突然某天需要增加审批订单的功能,其他订单功能都受影响,就需要阅读一大坨代码,然后又是增加代码,又是修改代码的,都在一个函数下,非常不美观(一看到这么多代码,会比较泄气),也不好维护。
低内聚示例
from flask import Flask
app = Flask(__name__)@app.route("/order", methods=["GET"])defquery_order():return"查询订单"@app.route("/order", methods=["POST"])defcreate_order():return"生成订单"@app.route("/order", methods=["PUT"])defmodify_order():return"修改订单"@app.route("/order", methods=["DELETE"])defdelete_order():return"删除订单"@app.route("/approval", methods=["POST"])defapprovaled():return"审批通过"@app.route("/approval", methods=["DELETE"])defreject():return"审批驳回"
该示例中,有/order接口的不同请求方法,有/approval接口的不同请求方法,总之,都是一个接口路径对应多个请求方法。在该示例中,代码没有复用性,维护起来比较杂乱,包含了操作人员的订单管理(增删改查)、审批人员的订单管理(审批通过、审批驳回),归纳起来,可以分为2个维度:操作人员的订单管理、订单的审批流程。如果一个模块有很多个功能,有许多个维度,则维护成本也是逐渐增加,且不易分辨,需要去看注释或代码来确定当前函数的处理内容。
RESTFul风格示例
设计过程中需要遵循
复用性、高内聚、低耦合
,RESTFul风格规范会根据请求方式来设计不同的逻辑。
请求方式说明GET获取服务器资源POST新增服务器资源PUT更新服务器资源(客户端提供改变后的完整资源)DELETE删除服务器资源
示例:
from flask import Flask
from flask_restx import Api, Resource
app = Flask(__name__)
api = Api(app)@api.route("/order")classOrderOpt(Resource):defget(self):return"查询订单"defpost(self):return"生成订单"defput(self):return"修改订单"defdelete(self):return"删除订单"@api.route("/approval")classApprovalProcess(Resource):defpost(self):return"审批通过"defdelete(self):return"审批驳回"
在该示例中,从2个维度去维护订单功能:操作人员的订单管理、订单的审批流程,虽然订单管理和审批流程之间可能会有影响,但从一定程度上降低了高耦合,另一方面也更好的进行维护管理。
flask-restx插件集成swagger
一般而言,开发在设计、开发接口的时候,需要提供接口文档给到其他开发进行联调,或给到测试进行接口测试。flask-restx插件集成了swagger,可以对接口进行模块管理,也可对接口文档进行配置。
flask-restx插件集成swagger,依赖于namespace的使用。
namespace的使用
在 RESTFul风格示例 的示例中,如果我们访问接口根路径,发现界面如下:
从图中可以看出,接口都在default namespace下,如果接口很多的话,就不利于管理,flask-restx插件的namespace可以对接口进行分类管理。
使用namespace进行接口分类管理,需要进行如下步骤:
- 步骤1:定义Namespace对象
- 步骤2:为类添加装饰器:Namespace对象的route()方法
- 步骤3:Api对象添加Namespace对象访问路径
使用到的类或方法说明如下:
Namespace类:
- 定义对象:
Namespace(name, description=None, path=None, decorators=None, validate=None, authorizations=None, ordered=False, **kwargs)
- 参数说明: - name:分类名称- description:分类描述- path:前置路径(后面会根据步骤2的装饰器和步骤3的路径设置决定接口的完整路径)
@Namespace().route(“”)是将类归纳到某个Namespace对象下,即将类对应的接口归纳到某个层级下,route()可以设置子路由,受步骤1中Namespace的path参数和步骤3的add_namespace()方法影响,
如果不设置子路由,则传入空字符串(不要什么都不传)
Api对象的
add_namespace(ns, path=None)
是将对应的Namespace对象ns添加到flask服务中(如果不加则识别不到对应类的接口),并设置前置路径path。
关于namespace的接口分类管理,我们从步骤1到步骤3可以看到,有3个地方设置路由,分别是Namespace实例化时的参数path、装饰器Namespace对象的route()方法、Api对象的add_namespace()方法中的path参数,关于这3个地方对对应接口的接口路径的最终结果,如下:
- 如果Api对象的add_namespace()方法中的path参数有传入,则该path参数值作为前置路径
- 如果Api对象的add_namespace()方法中的path参数无传入 - 如果Namespace实例化时有传入参数path,则path值会被作为前置路径- 如果Namespace实例化时未传入参数path,则默认为Namespace实例化时的分类名称作为前置路径
- 接口完整路径是:前置路径 + route()方法传入的参数值
示例:
py脚本内容如下:
from flask import Flask
from flask_restx import Api, Resource, Namespace
app = Flask(__name__)
api = Api(app)# 定义Namespace实例
ns1 = Namespace("demo management","add_namespace()和Namespace()都有path参数传入", path="/name1")
ns2 = Namespace("demo2 management","add_namespace()无path参数传入,Namespace()有path参数传入", path="/name2")
ns3 = Namespace("demo3 management","add_namespace()和Namespace()都无path参数传入")
ns4 = Namespace("demo4 management","add_namespace()和Namespace()都有path参数传入,route()传入空字符串", path="/name4")
ns5 = Namespace("demo5 management","add_namespace()和Namespace()都有path参数传入,route()未传参", path="/name5")@ns1.route("/route1")classDemo1(Resource):defget(self):return"demo1 get"defpost(self):return"demo1 post"@ns2.route("/route2")classDemo2(Resource):defget(self):return"demo2 get"defpost(self):return"demo2 post"@ns3.route("/route3")classDemo3(Resource):defget(self):return"demo3 get"@ns4.route("")classDemo4(Resource):defget(self):return"demo4 get"@ns5.route()classDemo5(Resource):defget(self):return"demo5 get"
api.add_namespace(ns1,"/api_add")
api.add_namespace(ns2)
api.add_namespace(ns3)
api.add_namespace(ns4,"/api_add4")
api.add_namespace(ns5,"/api_add5")if __name__=="__main__":
app.run()
执行脚本(启动flask服务),访问接口根路径,结果如下:
swagger接口文档配置
flask-rest中swagger文档配置有2种方式:
- 第一种:使用
@Api对象.doc()
或者@namespace对象.doc()
装饰请求方法 - 第二种:使用
parser = api.parser()
配合`@api.expect(parser)装饰器入参的校验和传入
推荐使用第二种方式
doc()方式
py脚本内容是:
from flask import Flask, request
from flask_restx import Api, Resource, Namespace, fields
app = Flask(__name__)
api = Api(app)
ns = Namespace("分类名称", description="分类的描述")@ns.route("")classDemo(Resource):# doc()种对请求参数params(即url参数)进行字段说明@ns.doc(params={"id":"用户编号","subject":"科目"})defget(self):return{"code":0,"data": request.args}#
post_check_model = api.model("PostModel",{"name": fields.String(description="姓名", required=True),"age": fields.Integer(min=0),"gender": fields.String(description="性别", enum=["男","女"])})@ns.doc(body=post_check_model)defpost(self):
data = request.json
returnf"{data.get('name')}, {data.get('age')}岁, {data.get('gender')}性"
api.add_namespace(ns,"/demo")if __name__=="__main__":
app.run(debug=True)
执行脚本(启动flask服务),访问根路径:
doc()的方式相当于对接口字段进行了描述,并没有很好的对接口字段进行限制,所以不推荐这种方式。
Api对象parser方式
比较推荐的swagger接口文档配置步骤如下:
- 步骤1:在类中,通过Api对象的
parser()
方法获取RequestParser对象 - 步骤2:在RequestParser对象中,通过
add_argument()
方法对接口请求字段进行设置 - 步骤3:在对应的请求方法中,添加namespace对象的装饰器,并通过
expect()
方法将RequestParser对象传入,表明该接口使用该RequestParser对象的接口字段定义。
RequestParser对象的
add_argument(*args, **kwargs)
方法是对接口请求字段进行配置,相关的关键参数说明如下:
- 第一个参数是参数名,即接口请求字段的字段名称
- 后面是关键字传参,常用的关键字有: - type:类型 - 参数值:int(整型),bool(布尔型)、float(浮点型)、string(字符串)、FileStorage(文件对象)- 说明:对接口字段值的类型进行控制,指定接口字段值的数据类型- required:约束控制 - 参数值:True、False- 说明:如果设置了为True,则表示该字段是必传的,False表示非必传- help:字段说明,字符串,可以对字段进行说明- choices:枚举参数 - 说明:参数值应该是个列表、元组等对象,表示字段值必须在指定的范围中- location:对应flask.request对象中的属性 - 参数值:参数值是flask.request对象中的属性,比如args、form、json、files、headers等- 说明:用来指定该字段所在位置,比如在url请求参数、form表单或json数据中
示例:
from flask import Flask, request
from flask_restx import Api, Resource, Namespace
from werkzeug.datastructures import FileStorage
app = Flask(__name__)
api = Api(app)
ns = Namespace("分类名称", description="分类的描述")@ns.route("")classDemo(Resource):# 定义RequestParser解析器对象
get_parser = api.parser()# 通过RequestParser对象添加接口请求字段参数配置
get_parser.add_argument("id",type=int,help="身份证号", location="args")
get_parser.add_argument("subject",type=str,help="科目", location="args")# 通过Namespace对象的expect(RequestParser对象)方法,对get请求进行装饰(即对该get请求接口字段指定字段配置@ns.expect(get_parser)defget(self):return{"code":0,"args_data": request.args}
post_parser = api.parser()
post_parser.add_argument("file_data",type=FileStorage,help="上传文件", location="files")
post_parser.add_argument("account",type=str,help="帐号", location="form")
post_parser.add_argument("password",help="密码", location="form")@ns.expect(post_parser)defpost(self):return{"status_code":"success","form_data": request.form,"file_name": request.files.get("file_data").filename}
put_parser = api.parser()
put_parser.add_argument("name",type=str,help="姓名", location="json", required=True)
put_parser.add_argument("age",type=int,help="年龄", location="json")
put_parser.add_argument("gender",help="性别", choices=["男","女"], location="json")@ns.expect(put_parser)defput(self):return{"code":0,"message":"success","json_data": request.json}
api.add_namespace(ns,"/demo")if __name__=="__main__":
app.run(debug=True)
执行脚本(启动flask服务),访问根目录并发送请求,结果如下:
注意:Swagger 的检验比较虚(即字段校验一般不生效),真要强制约束请求信息,还是要在代码里
版权归原作者 wenxiaoba 所有, 如有侵权,请联系我们删除。