• 测试需求平台4-Flask实现API服务入门实战


    ✍此系列为整理分享已完结入门搭建《TPM提测平台》系列的迭代版,拥抱Vue3.0将前端框架替换成字节最新开源的arco.design,其中约60%重构和20%新增内容,定位为从
    0-1手把手实现简单的测试平台开发教程,内容将囊括基础、扩展和实战,由浅入深带你实现测试开发岗位中平台工具技术能力入门和提升。

    1.Python服务框架

    目前Python开发中流行个比较受流行服务框架有Tornado,Django,Fastapi,Flask,概括来讲Tornado性能高,Django大而全、flask小而精,Fastapi快速,以下给出来自官方和网络总结的说明和最小启动代码案例。

    Tornado 是一个 Python Web 框架和异步网络库,性能优越,非阻塞的设计方式。

    官方 https://www.tornadoweb.org/en/stable/

    • 适合性能要求高服务开发
    import tornado.ioloop
    import tornado.web
    
    class MainHandler(tornado.web.RequestHandler):
        def get(self):
            self.write("Hello, Tornado")
    
    def make_app():
        return tornado.web.Application([
            (r"/", MainHandler),
        ])
    
    if __name__ == "__main__":
        app = make_app()
        app.listen(8888)
        tornado.ioloop.IOLoop.current().start()
    

    Django 是基于Python 语言开发的一套重量级Web框架,其设计的初衷就是为了帮助开发人员以最小的代码量快速建站。

    官方 https://docs.djangoproject.com/en/4.0/

    • 重web框架功能齐全,提供一站式解决的思路;
    • 自带ORM和模板引擎,支持jinja等非官方模板引擎;
    • 成熟稳定,开发效率高,Django的整体封闭性比较好,适合大型网站开发。

    模版代码有点多,直接使用IDE选择DJango模版进行创建,初始化后看下代码和运行效果截屏2022-07-03 22.13.30.png

    FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框架。

    官方 https://fastapi.tiangolo.com/

    • 快速:可与 NodeJS 和 Go 比肩的极高性能;
    • 高效编码:提高功能开发速度约 200% 至 300%;
    • 更少 bug:减少约 40% 的人为(开发者)导致错误;
    • 智能:极佳的编辑器支持。处处皆可自动补全,减少调试时间;
    • 简单:设计的易于使用和学习,阅读文档的时间更短;
    • 简短:使代码重复最小化。通过不同的参数声明实现丰富功能;
    • 健壮:生产可用级别的代码。还有自动生成的交互式文档。
    from fastapi import FastAPI
    
    app = FastAPI()
    
    @app.get("/")
    async def root():
        return {"message": "Hello FastAPI"}
    

    Flask 是轻量级的框架,自由、灵活、可扩展性强,核心基于Werkzeug WSGI工具和jinja2模板引擎。

    官方 https://flask.palletsprojects.com/

    • 适用于做小网站以及web服务的API

    本项目将采用此框架,所以这里不做举例,留在第2小节细细道来。

    对于几大框架的选择,从大奇的个人来看都无所谓,对于实现测试团队内部的平台服务,任何一个框架都能满足,工作中选择什么框架可能还需要看所在的团队的历史项目、技术选项、甚至是技术负责人的指定,而对于仅个人学习也是把重点放在掌握学习方法,及如何运用上,一定不要限定在学这门语言本身。

    2.Flask框架

    2.1 最小应用详解

    从系列第1篇和上边小结中算是对Flask有了基本了解,这里让我们再从一个最小的 Flask 应用细化讲解,创建一个app.py也是程序代码的运行起点。

    from flask import Flask
    
    app = Flask(__name__)
    
    @app.route("/api/sayHello/")
    def hello_world():
        return "Hello, TPM!"
    
    if __name__ == '__main__':
        app.run()
    

    这一小段代码最简单的实现了一个默认的GET请求接口 /api/sayHello,没有请求参数,返回的是一个“Hello,TPM”字符串,对其详细分解

    1. 首先是引入Flask类
    2. 然后创建了该类的一个实例,该实例将成为一个Web服务器网关接口( Python Web Server Gateway Interface,缩写为 WSGI )应用
    3. 使用 装饰器 route() 来告诉 Flask 触发函数 的 URL ,默认HTTP请求方法为GET类型
    4. 被修饰的方法实现返回一个字符串,默认返回 text/htm 类型
    5. Python程序的主方法,程序执行入口(次代码中省略也可以运行)

    理解上边的代码后,开启一个终端,执行方法 $ flask run 启动应用,浏览器地址输入 http://127.0.0.1:5000/ 即可看到返回的字符串 “Hello,TPM!”

    app.run() 还涉及到几个参数,扩展介绍下,如果你使用类似PyCharmm开发工具,使用command + 鼠标左键 就可以点击跳转方法的实现,方法如下,如果英文好的可以直接官方解释。

    def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):
    

    这里可以设置参数分别是host-指定IP,port-指定端口,debug-调试模式

    1. host设定默认的host为本地地址127.0.0.1 只能本地访问调试,如果局域网其他机器允许访问,就需要设置host=本机IP,或者host=0.0.0.0让其自行识别
    2. port设定默认port为5000端口,如果想用其他端口需要给定此参数如port=8082
    3. debug设定通过debug=True设定调试模式,这样每次有代码修改保存时程序会自动重新热加载,不需要每次重新启动。

    一个简单的配置如下:

    app.run(host="0.0.0.0", port=8082, debug=True)
    

    这里需要特别注意的是,方法配置必须使用python3 app.py去启动程序,如果是用上述 flask run 命令或者PyCharm启动,方法里设置参数是无效的,需要通过指定参数 flask run --host=0.0.0.0 --port=8082 或者配置参数。

    2.2 Flask的路由和HTTP方法

    路由 顾名思义就是给定请求路径,在最小应用的例子中已经说过是通过route() 装饰器 设置URL,方法就是给定第一个字符串参数如/api/project/select,至于如何配置参数规则下边具体讲。

    HTTP方法 常见有GET、POST、DELETE,其实前两种在Resful api实现中使用最频繁,定义接口的请求方法也很简单就是在路由内指定参数 methods=[‘方法’]

    @app.route("/api/product/search",methods=['GET'])
    def product_search():
        return "GET接口请求查询产品操作"
    
    @app.route("/api/product/create",methods=['POST'])
    def product_create():
        return "POST接口请求新增产品操作"
    
    @app.route("/api/product/delete", methods=['DELETE'])
    def product_delete():
        return "Delete接口请求新增产品操作"
    

    这些代码加在最小程序app.py重新运行(debug=True自动重新加载),用Postman分别用对应的方法请求,都会正常返回return中的给定的字符串内容,这里可以尝试下如果一个接口设定了POST类型,如果测试用其他类型,则会返回 405 Method Not Allowed 表示不被允许的请求方法。

    我们注意到methods=[]是个数组,所以我们可以对一个api指定多种类型的,比如给定GET和POST这样客户端用对应哪种方法请求都会正常返回值。

    @app.route("/api/product/list",methods=['GET','POST'])
    def product_list():
        return "我支持GET和POST两种"
    

    2.3 接口参数

    演示和总结一下客户端传参和服务器获取参数的几种常见方式。

    1)GET通过 request.args 获取params值
    请求传参在路径中,形式为?key=value,多个参数用&链接更多键值对,后端通过**request.args**给定key获取,如果没有匹配的为None

    from flask import request
    @app_product.route("/api/product/search",methods=['GET'])
    def product_search():
        # 获取?后指定的title值,没有为None
        title = request.args.get('title')
    
        return {'tilte':title}
    

    运行请求测试验证能否正常从路径中获取对应参数值

    2)POST通过 requext.form 获取form值
    也可以用arg获取所有参数和指定参数,具体解释看代码注释

    from flask import request
    
    @app_product.route("/api/product/create",methods=['POST'])
    def product_create():
        # 获取?后指定title=的值,取不到默认为None
        title = request.args.get('title')
        # args 获取所有URL?后边的参数和值
        args = request.args
        print(args)
        # 获取Post format格式的值(缺失会报错)
        keyCode = request.form["keyCode"]
    
        return {'title': title, 'keyCode': keyCode}
    

    新增Form-data请求测试:

    3)POST通过 request.get_data() 获取json body参数
    通常请求header设置content-type: application/json格式,代码中因此也可以通过request.json.get("key") 获取body内指定关键词的值。

    from flask import request
    @app_product.route("/api/product/update",methods=['POST'])
    def product_update():
        # 获取body中某个值,取不到默认为None
        keyCode = request.json.get('keyCodes')
        print(keyCode)
    
        # 获取整个json字符串体
        body = request.get_data()
        print(type(body))
    
        return body
    

    对此POST接口执行标准的JSON body数据格式请求,请求测试如下:

    4)路径的参数形式
    这里再扩展一个可能用到了传参方式,此方式可以严格限制传递的类型,方式在 route path 定义<类型:关键词>,然后通过方法内同变量名获取。

    @app.route('/api/project/remove/',methods=['DELETE'])
    def project_remove(project_id):
        print(project_id)
        return '我是从路径获取并且只接收int类型:{}'.format(project_id)
    

    进行delete方法请求测试结果如下

    2.4 接口模块化

    从上边的定义很多接口可以看到我们所有的定义都编写在主程序类里,这样对于稍微复杂的应用程序代码就会很臃肿,现在编程都都讲究各种模式或者分模块编程,优化方案为使用 blueprints 网络中文译为蓝图,以上边的例子来优化,将所有/api/product/* 的接口全部抽出来放到一个product.py 文件中,并定义一个别名为app_product 蓝图。

    from flask import Blueprint
    
    app_product = Blueprint("app_product", __name__)
    
    @app_product.route("/api/product/search",methods=['GET'])
    def product_search():
        return "GET接口请求查询产品操作"
    
    @app_product.route("/api/product/create",methods=['POST'])
    def product_create():
        return "POST接口请求新增产品操作"
    
    @app_product.route("/api/product/delete", methods=['DELETE'])
    def product_delete():
        return "Delete接口请求新增产品操作"
    
    @app_product.route("/api/product/list",methods=['GET','POST'])
    def product_list():
        return "我支持GET和POST两种"
    

    接着就要在app.py 注册 blueprint,保存自动运行

    from flask import Flask
    # 导入模块类
    from apis.products import app_product
    
    app = Flask(__name__)
    # 注册blueprint
    app.register_blueprint(app_product)
    
    @app.route("/api/sayHello/")
    def hello_world():
        return "Hello, TPM!"
    
    if __name__ == '__main__':
        app.run(host='0.0.0.0', port=8082, debug=True)
    

    再次通过postman请求之前几个接口,一切正常,但看上去是不是清爽很多。

    同样我们跳转 blueprints.py 源码查看 init 方法还有不少参数,比如 url_prefix="/api/product" 定义统一URL前缀,那么在 route 路径定义都可以去掉相同的前缀路径 /api/product,这样更清爽了,至于其他参数后续涉及到再讲解。

    # 抽离类接口代码
    from flask import Blueprint
    
    app_product = Blueprint("app_product", __name__)
    
    @app_product.route("/search",methods=['GET'])
    def product_search():
        return "GET接口请求查询产品操作"
    
    #.....
    # app.py 主代码, 注册时增加前缀
    app.register_blueprint(app_product, url_prefix="/api/product")
    
    

    相信通过这篇对于Flask讲解和实战,接下后端服务开发中应该更有信心了,如果还没有完全弄明白也无所谓,因为后续的系列分享中会反复的用到,写多了自然也就会了,学习本身也是一个循序渐进的过程,不过复习也是很重要的,所以特别指出此篇对于Flask的API实现应用很重要,如果后边时间长了忘了记得多回来查阅。

  • 相关阅读:
    将fbx文件转换成gltf格式的模型文件
    Python-----PyInstaller的简单使用
    这是JWT 简单使用
    【论文阅读】时序动作检测系列论文精读(2019年)
    AQS之CountDownLatch分析 (八)
    【数据结构】-----二叉树(递归、层次实现二叉树的遍历)
    MySQL监控innodb的阻塞
    利用java语言将csv格式数据导入mysql数据库
    JavaScript-修炼之路第五层
    Ajax学习:解决跨域_JSONP
  • 原文地址:https://blog.csdn.net/zyueqi1987/article/details/127043496