1. 痛点

随着业务的飞速发展,API接口越来越多,路由管理文件从几十号变成几百上千行,且每次上新服务,需要在修改路由文件代码,带来一定的风险。

2. 解决方案

  • 既然路由文件随着业务的扩展越来越庞大,那就去掉路由文件。
  • 制定对应规则,路由通过API文件名根据一定的规则对应类名,然后自动导入对应实现类,注册到Web框架中。

2.1 制定规则

下面这套规则只是其中一种方案,可以针对项目情况制定对应的规则,然后实现相关代码,但是整体思路基本一样。

  1. 代码目录结构,列一下简单的项目文件目录,下面以flask框架为例:

app.py是启动文件。 resources是API接口代码文件夹。 services是为API接口服务的函数封装文件夹。 如果项目还有依赖文件,也可以单独再建其他文件夹。

  1. 项目的API接口代码均放在resources文件夹下,且此文件夹只能写接口API服务代码。
  2. 接口名称命名以_连接单词,而对应文件里的类名文件名称的单词,不过换成是驼峰写法。
  3. 类的导入则通过文件名对应到类名,实现自动映射注册到web框架中。

规则举例如下: 如上图,resources下有一个hello_world接口,还有一个ab项目文件夹,ab下面还有一个hello_world_python接口以及子项目文件夹testab,testab下面也有一个hello_world_python.

  • 接口文件的文件名命名规范: 文件名命名均为小写,多个单词之间使用’_'隔开,比如hello_world.py 命名正确,helloWorld.py命名错误。

  • 接口文件里的接口类Class命名是以文件名字转为驼峰格式,且首字母大写。比如hello_world.py 对应的接口类是 HelloWorld 举例: hello_world.py

    hello_world_python.py

  1. 路由入口文件会自动映射,映射规则为: 前缀 / 项目文件夹[…] / 文件名

    其中 前缀为整个项目的路由前缀,可以定义,也可以不定义,比如api-ab项目,可以定义整个项目的路由前缀为 ab/ resource下面项目文件夹如果有,则会自动拼接,如果没有,则不会读取。 举例: 前缀为空,上图resources中的三个接口对应的路由为:

    hello_world.py ==>  /hello_world
    ab/hello_world_python.py ==> /ab/hello_world_python
    ab/testab/hello_world_python.py ==> /ab/testab/hello_world_python

    前缀为ab/,上图resources中的三个接口对应的路由为:

    hello_world.py ==> ab/hello_world
    ab/hello_world_python.py ==> ab/ab/hello_world_python
    ab/testab/hello_world_python.py ==> ab/ab/testab/hello_world_python
    复制代码
    
  2. 关于resources里目录结构,代码里是可以允许N层,但建议不要超过3层, 不易管理。

2.2 代码实现

python很多框架的启动和路由管理都很类似,所以这套规则适合很多框架,测试过程中有包括flask, tornado, sanic, japronto。 以前年代久远的web.py也是支持的。

完整代码地址: github.com/CrystalSkyZ…

  1. 实现下划线命名 转 驼峰命名 函数,代码演示:

    def underline_to_hump(underline_str):
    '''
    下划线形式字符串转成驼峰形式,首字母大写
    '''
    sub = re.sub(r'(_\w)', lambda x: x.group(1)[1].upper(), underline_str)
    if len(sub) > 1:return sub[0].upper() + sub[1:]
    return sub
  2. 实现根据字符串导入模块函数, 代码演示:

    • 通过python内置函数__import__函数实现加载类
    #更多Python视频、源码、资料加群683380553免费获取
    def import_object(name):
    """Imports an object by name.
    import_object('x') is equivalent to 'import x'.
    import_object('x.y.z') is equivalent to 'from x.y import z'.
    """
    if not isinstance(name, str):name = name.encode('utf-8')
    if name.count('.') == 0:return __import__(name, None, None)parts = name.split('.')
    obj = __import__('.'.join(parts[:-1]), None, None, [parts[-1]], 0)
    try:return getattr(obj, parts[-1])
    except AttributeError:raise ImportError("No module named %s" % parts[-1])
    复制代码
    
    • 通过importlib模块实现
    importlib.import_module(name)
    复制代码
    

    上面2种方法都可以,github上代码里2种方法都有测试。

  3. 检索resources文件夹,生成路由映射,并导入对应实现类, 代码演示如下:

    def route(route_file_path,resources_name="resources",route_prefix="",existing_route=None):route_list = []def get_route_tuple(file_name, route_pre, resource_module_name):""":param file_name: API file name:param route_pre: route prefix:param resource_module_name: resource module"""nonlocal route_listnonlocal existing_routeroute_endpoint = file_name.split(".py")[0]#module = importlib.import_module('{}.{}'.format(#    resource_module_name, route_endpoint))module = import_object('{}.{}'.format(resource_module_name, route_endpoint))route_class = underline_to_hump(route_endpoint)real_route_endpoint = r'/{}{}'.format(route_pre, route_endpoint)if existing_route and isinstance(existing_route, dict):if real_route_endpoint in existing_route:real_route_endpoint = existing_route[real_route_endpoint]route_list.append((real_route_endpoint, getattr(module, route_class)))def check_file_right(file_name):if file_name.startswith("_"):return Falseif not file_name.endswith(".py"):return Falseif file_name.startswith("."):return Falsereturn Truedef recursive_find_route(route_path, sub_resource, route_pre=""):nonlocal route_prefixnonlocal resources_namefile_list = os.listdir(route_path)if config.DEBUG:print("FileList:", file_list)for file_item in file_list:if file_item.startswith("_"):continueif file_item.startswith("."):continueif os.path.isdir(route_path + "/{}".format(file_item)):recursive_find_route(route_path + "/{}".format(file_item), sub_resource + ".{}".format(file_item), "{}{}/".format(route_pre, file_item))continueif not check_file_right(file_item):continueget_route_tuple(file_item, route_prefix + route_pre, sub_resource)recursive_find_route(route_file_path, resources_name)
    if config.DEBUG:print("RouteList:", route_list)return route_list
    • get_route_tuple函数作用是通过字符串导入类,并将路由和类以元组的方式添加到数组中。
    • check_file_right函数作用是过滤文件夹中不合法的文件。
    • recursive_find_route函数采用递归查找resources中的文件。
    • existing_route参数是将已经线上存在的路由替换新规则生成的路由,这样旧项目也是可以优化使用这套规则。

3. 应用到项目中

以flask框架为例,其余框架请看github中的代码演示。 app.py 中代码

app = Flask(__name__)
api = Api(app)
# APi route and processing functions
exist_route = {"/flask/hello_world": "/hello_world"}
route_path = "./resources"
route_list = route(route_path,resources_name="resources",route_prefix="flask/",existing_route=exist_route)for item in route_list:api.add_resource(item[1], item[0])if __name__ == "__main__":app.run(host="0.0.0.0", port=int(parse_args.port), debug=config.DEBUG)
复制代码

运行app.py之后,路由打印如下:

RouteList: [
('/hello_world', <class'resources.hello_world.HelloWorld'>),\   ('/flask/ab/testab/hello_world_python_test', <class 'resources.ab.testab.hello_world_python_test.HelloWorldPythonTest'>), \
('/flask/ab/hello_world_python', <class 'resources.ab.hello_world_python.HelloWorldPython'>)
]
复制代码

元组第一个元素则是路由,第二个元素是对应的实现类。


更多Python视频、源码、资料加群683380553免费获取
总结: 至此,通过制定一定规则,解放路由管理文件方案完成。

Python Web开发: 教你如何解放路由管理相关推荐

  1. 手把手教你写网站:Python WEB开发技术实战

    摘要:本文详细介绍了Python WEB开发的基础入门.以一个博客站点的开发为例讲解了基于Django框架开发WEB站点的全过程.通过本文的学习可以快速掌握基于Django的Python WEB的开发 ...

  2. pythonweb开发-手把手教你写网站:Python WEB开发技术实战

    摘要:本文详细介绍了Python WEB开发的基础入门.以一个博客站点的开发为例讲解了基于Django框架开发WEB站点的全过程.通过本文的学习可以快速掌握基于Django的Python WEB的开发 ...

  3. python web-手把手教你写网站:Python WEB开发技术实战

    摘要:本文详细介绍了Python WEB开发的基础入门.以一个博客站点的开发为例讲解了基于Django框架开发WEB站点的全过程.通过本文的学习可以快速掌握基于Django的Python WEB的开发 ...

  4. python开发一个自己的技术网站_手把手教你写网站:Python WEB开发技术实战

    摘要:本文详细介绍了Python WEB开发的基础入门.以一个博客站点的开发为例讲解了基于Django框架开发WEB站点的全过程.通过本文的学习可以快速掌握基于Django的Python WEB的开发 ...

  5. [转载] Python Web开发—进阶提升 490集超强Python视频教程 真正零基础学习Python视频教程

    参考链接: 在Python中创建代理Web服务器 2 Python Web开发-进阶提升 490集超强Python视频教程 真正零基础学习Python视频教程 [课程简介] 这是一门Python We ...

  6. Python Web开发:Django+BootStrap实现简单的博客项目

    创建blog的项目结构 关于如何创建一个Django项目,请查看[Python Web开发:使用Django框架创建HolleWorld项目] 创建blog的数据模型 创建一个文章类 所有开发都是数据 ...

  7. Python Web开发——Django框架学习

    文章目录 Web开发简介 Web应用技术 Web前端开发 Web后端开发 使用 Django 开发后端服务 项目目标 管理员操作 登录界面 客户管理界面 添加客户界面 药品管理界面 添加药品界面 订单 ...

  8. python web开发 网络编程 TCP/IP UDP协议

    文章目录 1. TCP/IP协议 1.1 IP协议 1.2 TCP协议 2. UDP协议 3. Socket 4. TCP编程 4.1 创建TCP服务器 4.2 创建TCP客户端 4.3 简易聊天工具 ...

  9. python在线搭建教程_理解python web开发,轻松搭建web app!

    大家好,今天分享给大家的是理解python web开发,轻松搭建web app,希望大家学有所获! 因为 python代码的优雅美观且易于维护这一特点,越来越多的人选择使用 Python做web开发. ...

最新文章

  1. python3.6 - threading 多线程编程进阶,线程间并发控制(2)
  2. 贝壳测试|新赛道、新电源
  3. pcl 平面分割 RANSAC
  4. asp.net中DropDownList的SelectedIndexChanged触发问题
  5. 全文检索、数据挖掘、推荐引擎系列5---文章术语向量表示法
  6. Linux内核分析--操作系统是如何工作的
  7. 关于mysql报 loopWaitCount 0, wait millis 60000 错误的解决办法
  8. mysql 回滚失败_浅析Mysql 数据回滚错误的解决方法
  9. Android数据的四种存储方式SharedPreferences、SQLite、Content Provider和File (四) —— ContentProvider...
  10. python集合以及编码初识
  11. Springboot视图解析与模板引擎
  12. android页指示动画,Android动画之翻页
  13. cassandra vs. voldemort
  14. EXCEL中多行多列数据与一行或一列数据的互相转换
  15. K-Means聚类算法原理及其python和matlab实现
  16. 默认选中 input元素默认选中设置
  17. 最大子序列之和(C语言)
  18. html文本框后面紧挨着按钮,HTMLbody标签中的相关标签2
  19. linux的echo连接字符串输出,echo命令 – 输出字符串或提取She
  20. BZOJ 1029: [JSOI2007]建筑抢修【贪心】

热门文章

  1. 【华为ensp实战案例——路由网络的构建与配置】
  2. 计算机插u盘抖动,u盘插入电脑闪蓝屏怎么办? 爱问知识人
  3. h5前端调用android拍照功能,H5调用Android拍照和摄像以及选取相册
  4. 基金投资组合中的夏普率
  5. SAP算号器 license key Developer Access Key 完美解决方案(备用)
  6. python去除数据集里空格_Python将文本去空格并保存到txt文件中
  7. StringUtils.join的详解---LPF
  8. Linux 系统无法解析主机地址(或未知的名称或服务)
  9. Three.js-材质纹理详解
  10. 半径为2.11的圆球的体积python_python习题