写点什么

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

作者:MegaQi
  • 2022 年 9 月 13 日
    上海
  • 本文字数:4627 字

    阅读完需:约 15 分钟

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

此分享将会是一个系列分享,将使用最新的 Acro Vue 前端框架,和 Python Flask 服务从 0-1 手把手实现一个测试需求平台,内容将囊括基础、扩展和实战,由浅入深带你实现测试开发岗位中平台工具技术能力入门和提升。

1.Python 服务框架

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


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

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


  • 适合性能要求高服务开发

import tornado.ioloopimport 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 模版进行创建,初始化后看下代码和运行效果


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/<int:project_id>',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__)# 注册blueprintapp.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 实现应用很重要,如果后边时间长了忘了记得多回来查阅。

发布于: 刚刚阅读数: 7
用户头像

MegaQi

关注

InfoQ签约作者 |公众号: 大奇测试开发 2018.02.15 加入

分享一些系列测试平台开发教程,也记录一些自己研究的技术,遇到的问题,总之想分享一些干货内容,愿给这个行业贡献微不足道的力量,不断突破不断成长。

评论

发布
暂无评论
测试需求平台4-Flask实现API服务入门实战_测试平台开发教程_MegaQi_InfoQ写作社区