当前位置: 首页 > news >正文

测试需求平台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/<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__)
# 注册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实现应用很重要,如果后边时间长了忘了记得多回来查阅。

相关文章:

  • js单行代码-----dom
  • 模型压缩常用方法简介
  • css常用属性
  • 【Android】Android Binder进程间通信AIDL示例与过程分析
  • C#教程 - 模式匹配(Pattern matching)
  • 动手学习深度学习 05:深度学习计算
  • 状体模式-优雅解决物流状态
  • CTF之加密解密训练
  • gnome-terminal用法解析
  • ceres优化库的使用
  • TypeScript基础教程
  • docker安装Jenkins配置cicd
  • MQTT android配置
  • wPDF v5支持并改进旋转文本的逻辑
  • <Linux复习>基本指令及重要热键
  • Brief introduction of how to 'Call, Apply and Bind'
  • cookie和session
  • django开发-定时任务的使用
  • Github访问慢解决办法
  • gops —— Go 程序诊断分析工具
  • Hibernate【inverse和cascade属性】知识要点
  • 从伪并行的 Python 多线程说起
  • 构建二叉树进行数值数组的去重及优化
  • 前端工程化(Gulp、Webpack)-webpack
  • 如何正确配置 Ubuntu 14.04 服务器?
  • 深度学习入门:10门免费线上课程推荐
  • 使用 Xcode 的 Target 区分开发和生产环境
  • 一些关于Rust在2019年的思考
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • MPAndroidChart 教程:Y轴 YAxis
  • postgresql行列转换函数
  • TPG领衔财团投资轻奢珠宝品牌APM Monaco
  • ​【已解决】npm install​卡主不动的情况
  • ​TypeScript都不会用,也敢说会前端?
  • ​软考-高级-系统架构设计师教程(清华第2版)【第20章 系统架构设计师论文写作要点(P717~728)-思维导图】​
  • #{} 和 ${}区别
  • #ifdef 的技巧用法
  • (+3)1.3敏捷宣言与敏捷过程的特点
  • (day 12)JavaScript学习笔记(数组3)
  • (二)linux使用docker容器运行mysql
  • (十)【Jmeter】线程(Threads(Users))之jp@gc - Stepping Thread Group (deprecated)
  • (转)使用VMware vSphere标准交换机设置网络连接
  • ... fatal error LINK1120:1个无法解析的外部命令 的解决办法
  • .NET CORE 2.0发布后没有 VIEWS视图页面文件
  • .NET 使用 ILRepack 合并多个程序集(替代 ILMerge),避免引入额外的依赖
  • .NET牛人应该知道些什么(2):中级.NET开发人员
  • .net使用excel的cells对象没有value方法——学习.net的Excel工作表问题
  • /run/containerd/containerd.sock connect: connection refused
  • [ 转载 ] SharePoint 资料
  • []FET-430SIM508 研究日志 11.3.31
  • [acwing周赛复盘] 第 94 场周赛20230311
  • [AIGC] 使用Curl进行网络请求的常见用法
  • [BUAA软工]第一次博客作业---阅读《构建之法》
  • [C# WPF] 如何给控件添加边框(Border)?
  • [C#]使用DlibDotNet人脸检测人脸68特征点识别人脸5特征点识别人脸对齐人脸比对FaceMesh