flask入门教程之请求与响应

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("/")
def hello_world():
    return "

Hello World!

"

脚本中,需要导入Flask类,并创建Flask对象app,然后使用app.route()装饰器设置hello_world()的路由(即接口路径)。
脚本编辑好之后,启动该脚本的flask服务,切换到脚本所在目录,执行flask run命令:flask --app flask_hello run(flask_hello为脚本名称)
flask入门教程之请求与响应_第1张图片
flask入门教程之请求与响应_第2张图片

启动flask服务的命令,不同的flask版本不太一样,我这里是2.2.2版本的,如果想知道其他的版本的,可以去官网选择对应版本查看
flask入门教程之请求与响应_第3张图片
启动flask服务还有另一个方法,就是在脚本里写上启动代码:

from flask import Flask
app =  Flask(__name__)

@app.route("/")
def hello_world():
    return "

Hello World!

--python命令启动"
if __name__=="__main__": # 启动flask服务 app.run()

python命令执行该脚本,结果如下:
flask入门教程之请求与响应_第4张图片
flask入门教程之请求与响应_第5张图片

路由

我们访问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")
def search_videos():
    return "查找视频资源"

@app.route("/search/picture")
def search_pictures():
    return "查找图片资源"

if __name__=="__main__":
    app.run()

其中,/search/video/search/picture是定义的静态路由,访问时的接口路径无变动
flask入门教程之请求与响应_第6张图片

动态路由

有时候,接口路径是变动的,比如查询用户信息,查询用户A、用户B、用户C,并且用户信息不可控,不能每一个用户都造个路由,flask提供了动态路由,将变动的信息定义为一个变量。
添加路由时,将变动的部分使用<变量名>定义,然后被装饰的函数或方法,参数的名称与定义的变量名一致,这样函数或方法就可以使用该变量及其值。
示例:

from flask import Flask

app = Flask(__name__)

@app.route("/userinfo//info")
def search_videos(user_id):
    return f"这个是{user_id}的用户信息"

if __name__=="__main__":
    app.run()

执行脚本,启动flask服务后,访问如下:
flask入门教程之请求与响应_第7张图片
从结果中可以看出,/userinfo/后/info前的内容可以变动,也能处理成功。

限定类型

动态路由中,可以给变量限制其数据类型,格式:<类型:变量名>
数据类型有如下:

类型 描述
string (缺省值)接受任何不包含斜杠的文本
int 接受正整数
float 接受正浮点数
path 类似string,但可以包含斜杠
uuid 接受UUID字符串

示例:

from flask import Flask

app = Flask(__name__)

@app.route("/userinfo//info")
def search_videos(user_id):
    return f"用户编号{user_id}的信息如下:巴拉巴拉巴拉。。。"

if __name__=="__main__":
    app.run()

脚本执行后,flask服务启动,访问如下:
flask入门教程之请求与响应_第8张图片

在动态路由中的变量的数据类型限制比较简单,如果是复杂的校验,则可以在函数内部进行校验。

地址尾部的/

有时候我们在接口路径末尾加/和不加/的时候,访问是一致的,但有些加了/访问又报错,flask对路由尾部的/是有做处理的:

  • flask添加路由的尾部带有/:访问时加/和不加/效果是一样的
  • flask添加路由的尾部没有/:访问时不加/会报错,只能加/才能访问成功

示例如下:

from flask import Flask

app = Flask(__name__)

@app.route("/add1/")
def add1():
    return f"路由的末尾有'/'"

@app.route("/add2")
def add2():
    return f"路由的末尾没有'/'"

if __name__=="__main__":
    app.run()

启动flask服务后,执行结果如下:
flask入门教程之请求与响应_第9张图片

请求方式设置

请求方式有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"])
def only_get():
    return "只支持get请求方式"

@app.route("/post_and_get_demo/info", methods=["GET", "POST"])
def both_post_get():
    return "支持get和post的请求方式"

if __name__=="__main__":
    app.run()

启动对应的flask服务后,接口访问如下:
flask入门教程之请求与响应_第10张图片

请求数据处理

发送请求的时候,必定会接触到上传的参数,然后根据上传的参数进行逻辑处理。那我们怎么获取请求上传的数据呢?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"])
def get_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服务),发送请求
flask入门教程之请求与响应_第11张图片
控制台输出如下:
flask入门教程之请求与响应_第12张图片

请求参数处理

url链接中,有时会有请求参数,我们可以通过flask.request下的args属性获取到请求参数
示例:

from flask import Flask, request

wen = Flask(__name__)

@wen.route("/get/info", methods=["GET", "POST"])
def get_request_info():
    data = {}
    data.update(request.args)
    print(data)
    return data

if __name__ == "__main__":
    wen.run()

启动flask服务后,发送请求,查看响应报文:
flask入门教程之请求与响应_第13张图片
flask入门教程之请求与响应_第14张图片

请求体json数据处理

发送请求时,有时请求体数据是json数据,flask可以通过flask.request下的json属性获取到json请求体数据
脚本内容如下:

from flask import Flask, request

app = Flask(__name__)

@app.route("/post/info", methods=[ "POST"])
def post_json_data():
    print(f"请求json报文:{request.json}")
    return {"code": 0, "body_data": request.json}

if __name__ == "__main__":
    app.run()

启动flask服务后,发送post请求,结果如下:
flask入门教程之请求与响应_第15张图片
flask入门教程之请求与响应_第16张图片

表单请求数据处理

发送请求时,有时请求体数据是json数据,flask可以通过flask.request下的form属性获取到form表单数据
脚本内容如下:

from flask import Flask, request

app = Flask(__name__)

@app.route("/form/info", methods=[ "POST"])
def post_form_data():
    print(f"form数据:{request.form}")
    return {"code": 0, "form_data": request.form}

if __name__ == "__main__":
    app.run()

执行脚本,启动flask服务后,发送请求,相关结果如下:
flask入门教程之请求与响应_第17张图片
flask入门教程之请求与响应_第18张图片

文件请求数据处理

有些请求是上传文件,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"])
def post_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服务),发送文件上传的请求
flask入门教程之请求与响应_第19张图片
控制台输出内容为:
flask入门教程之请求与响应_第20张图片

响应数据设置

返回的响应报文有多种格式,比如文本、json、html等。

返回文本

响应报文如果是文本内容时,直接在函数内return字符串内容,flask会封装成文本格式的响应报文
示例:

from flask import Flask

app = Flask(__name__)

@app.route("/text", methods=[ "POST"])
def text_resp():
    return "响应报文内容是文本内容"

if __name__ == "__main__":
    app.run()

执行脚本后,postman发送请求如下:
flask入门教程之请求与响应_第21张图片
从响应报文中可以看到响应头的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"])
def response_status():
    return "响应报文内容是文本内容", 201

@app.route("/demo2", methods=[ "POST"])
def resp_json_status():
    return {"name": "wenxiaoba", "age": 18}, 201

@app.route("/demo3", methods=[ "POST"])
def resp_headers():
    return "设置了响应报文和响应头", {"token": "123456abcde", "address": "where are you"}

@app.route("/demo4", methods=[ "POST"])
def resp_status_headers():
    return "设置了响应报文和响应头", 202, {"token": "123456abcde", "address": "where are you"}

if __name__ == "__main__":
    app.run()

执行脚本(启动flask服务),发送请求,相关内容如下:
请求/demo1时,可以看到响应报文体和响应状态码是设置的内容
flask入门教程之请求与响应_第22张图片
请求/demo2时,响应报文体是json格式的
flask入门教程之请求与响应_第23张图片
flask入门教程之请求与响应_第24张图片
请求/demo3时,返回了设置的响应报文体,响应状态码是flask默认的200,响应头也有包含设置的字段
flask入门教程之请求与响应_第25张图片
flask入门教程之请求与响应_第26张图片
请求/demo4时,返回的响应报文体、状态码均为设置的,响应头也包含了设置的字段
flask入门教程之请求与响应_第27张图片
flask入门教程之请求与响应_第28张图片

返回json

大多数接口返回的响应数据是json格式,flask框架中,有2种方式返回json格式的响应报文:

  • 直接返回dict:flask底层会将dict转成json格式
  • 使用jsonify()方法,通过参数传入键值对或字典返回json数据,需要导入flask.jsonify

返回dict(即字典)数据时,客户端实际接收到的是json数据
flask入门教程之请求与响应_第29张图片
/jsonify1接口是通过jsonify()传入字典参数处理的响应报文
flask入门教程之请求与响应_第30张图片
/jsonify2接口是通过jsonify()传入键值对参数处理的响应报文
flask入门教程之请求与响应_第31张图片

返回其他格式

除了经常用的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"])
def get_html():
    return render_template('demo.html', name="wenxiaoba")

@app.route("/getxml", methods=["POST"])
def get_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>56price>
    body>
    <body>
        <book_name>{{book_name}}book_name>
        <author>不知道author>
        <price>62.3price>
    body>
book>

执行py脚本(启动flask服务),发送请求,结果如下:
flask入门教程之请求与响应_第32张图片
flask入门教程之请求与响应_第33张图片

设置额外数据——make_response()

如果想设置更多的响应信息,比如cookie,可以通过make_response()获取一个响应对象,可以在该对象中设置cookie、请求头等内容。
示例:
py脚本内容如下:

from flask import Flask, request, make_response, render_template

app = Flask(__name__)

@app.route("/setcookie", methods=["POST"])
def set_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服务)后,发送请求,结果如下:
flask入门教程之请求与响应_第34张图片
flask入门教程之请求与响应_第35张图片

启动服务配置

在前面例子中,我们基本上是通过脚本执行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"])
def demo():
    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模式在生效中
flask入门教程之请求与响应_第36张图片
我们发送请求到8888端口
flask入门教程之请求与响应_第37张图片
当我们对py脚本进行修改,并保存,保存的时候可以看到控制台重新加载了flask服务,这就是debug模式的优势,脚本编辑了之后,不用手动重新启动flask,只需要保存就会重新加载flask服务。
flask入门教程之请求与响应_第38张图片
flask入门教程之请求与响应_第39张图片

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')
class HelloWorld(Resource):     # 创建HelloWorld类,必须继承Resource模块
    def get(self):  # 定义RESTFul风格的get方法(对应get请求方式)
        return {'hello': 'world'}

if __name__ == '__main__':
    app.run(debug=True)

执行脚本(启动flask服务),发送请求,结果如下:
flask入门教程之请求与响应_第40张图片
根据示例中的注释,如果我们想定义一个接口,支持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")
class Person(Resource):
    def get(self):
        return "get方法不安全,没有权限查看个人信息"

    def post(self):
        name = request.json.get("name")
        return {"name": name, "age": 27, "gender": True}

if __name__=="__main__":
    app.run()

执行py脚本(启动flask服务)后,发送请求,结果如下:
get请求:
flask入门教程之请求与响应_第41张图片
post请求:
flask入门教程之请求与响应_第42张图片
如果请求方式非get或post,则报错
flask入门教程之请求与响应_第43张图片

添加路由

简单入门 的示例中,我们知道了怎么添加路由,其实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")
class demo(Resource):
    def post(self):
        return f"接口路径是:{request.path}"

if __name__=="__main__":
    app.run()

执行脚本(启动flask服务),发送请求,结果如下:
flask入门教程之请求与响应_第44张图片

add_resource()方法添加路由

add_resource()是给指定的类添加路由,示例如下:
py脚本内容如下:

from flask import Flask, request
from flask_restx import Resource, Api

app = Flask(__name__)
api = Api(app)

class Demo(Resource):
    def post(self, name):
        return {"path": request.path, "name": name}
# 给Demo类添加路由
api.add_resource(Demo, '/demo1/', '/demo2//info')

if __name__=="__main__":
    app.run()

执行脚本(启动flask服务),发送请求,结果如下:
flask入门教程之请求与响应_第45张图片

编写RESTFul风格的接口

在设计框架的时候,一般遵循复用性、高内聚、低耦合,即容易维护,减少冗余。
高耦合:可以理解为程序非常复杂难以维护,若修改了程序的某一处内容,涉及到方方面面(其他功能)都要跟着改,比如一个程序有100个函数执行正常,然而修改了其中1个函数,其他99个函数都要跟着修改,就是高耦合的场景。低耦合要求将完整的流程拆分成几个独立的模块、独立的功能,模块内的修改不影响模块之间的交互逻辑,比如商户订单管理和用户订单支付都是不同的模块,修改了商户订单管理的某个功能后,用户订单支付的大部分功能不需要修改。

高耦合示例

from flask import Flask, request

app = Flask(__name__)
@app.route("/demo", methods=["GET", "POST", "PUT", "DELETE"])
def demo():
    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"])
def query_order():
    return "查询订单"

@app.route("/order", methods=["POST"])
def create_order():
    return "生成订单"

@app.route("/order", methods=["PUT"])
def modify_order():
    return "修改订单"

@app.route("/order", methods=["DELETE"])
def delete_order():
    return "删除订单"

@app.route("/approval", methods=["POST"])
def approvaled():
    return "审批通过"

@app.route("/approval", methods=["DELETE"])
def reject():
    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")
class OrderOpt(Resource):
    def get(self):
        return "查询订单"

    def post(self):
        return "生成订单"

    def put(self):
        return "修改订单"

    def delete(self):
        return "删除订单"

@api.route("/approval")
class ApprovalProcess(Resource):
    def post(self):
        return "审批通过"

    def delete(self):
        return "审批驳回"

在该示例中,从2个维度去维护订单功能:操作人员的订单管理、订单的审批流程,虽然订单管理和审批流程之间可能会有影响,但从一定程度上降低了高耦合,另一方面也更好的进行维护管理。

flask-restx插件集成swagger

一般而言,开发在设计、开发接口的时候,需要提供接口文档给到其他开发进行联调,或给到测试进行接口测试。flask-restx插件集成了swagger,可以对接口进行模块管理,也可对接口文档进行配置。
flask-restx插件集成swagger,依赖于namespace的使用。

namespace的使用

RESTFul风格示例 的示例中,如果我们访问接口根路径,发现界面如下:
flask入门教程之请求与响应_第46张图片
从图中可以看出,接口都在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")
class Demo1(Resource):
    def get(self):
        return "demo1 get"

    def post(self):
        return "demo1 post"

@ns2.route("/route2")
class Demo2(Resource):
    def get(self):
        return "demo2 get"

    def post(self):
        return "demo2 post"

@ns3.route("/route3")
class Demo3(Resource):
    def get(self):
        return "demo3 get"

@ns4.route("")
class Demo4(Resource):
    def get(self):
        return "demo4 get"

@ns5.route()
class Demo5(Resource):
    def get(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服务),访问接口根路径,结果如下:
flask入门教程之请求与响应_第47张图片

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("")
class Demo(Resource):
    # doc()种对请求参数params(即url参数)进行字段说明
    @ns.doc(params={"id": "用户编号", "subject": "科目"})
    def get(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)
    def post(self):
        data = request.json
        return f"{data.get('name')}, {data.get('age')}岁, {data.get('gender')}性"

api.add_namespace(ns, "/demo")

if __name__=="__main__":
    app.run(debug=True)

执行脚本(启动flask服务),访问根路径:
flask入门教程之请求与响应_第48张图片

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("")
class Demo(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)
    def get(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)
    def post(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)
    def put(self):
        return {"code": 0, "message": "success", "json_data": request.json}

api.add_namespace(ns, "/demo")

if __name__=="__main__":
    app.run(debug=True)

执行脚本(启动flask服务),访问根目录并发送请求,结果如下:
flask入门教程之请求与响应_第49张图片
注意:Swagger 的检验比较虚(即字段校验一般不生效),真要强制约束请求信息,还是要在代码里

你可能感兴趣的:(flask,flask,python,后端)