基于uwsgi的Web服务路由功能升级

手上一个基于uwsgi开发的后台服务,接收GET请求,使用QUERY_STRING作为参数。

最开始的时候,路由功能使用的是if else的结构,大致如下

path = env["PATH_INFO"]
param = parse_query_string(env["QUERY_STRING"])
if path == "foo1/bar1":
    fooBar1(param)
elif path == "foo2/bar2":
    fooBar2(param)

为了方便管理并且美化代码,调整为使用路由字典的形式

funcion = {"foo/bar":fooBar}

functions[path](params)

def fooBar(params):
    param1 = params.get("p")

经过一段时间的使用后发现,在这种形式下每个函数的参数unpack与必要参数判断都需要独立进行,产生了很多重复代码

并且,在参考了flask等框架的装饰器形式之后,对路由部分进行了重构

def route(path="", required=None):
    def route_func(func):
        def params_check_func(params):
            args = inspect.getargspec(func)
            for required_param in (var_required if required else []):
                if required_param not in params:
                    raise ParamsError("%s is required" % required_param)
            return func(**params)
        global functions
        functions[path] = params_check_func
        return params_check_func
    return route_func

@route("foo/bar", ["p"])
def fooBar(p, *args, **kwargs):
    return do_sth(p)

其中因为参数利用了Python的可变变量功能,直接从QUERY_STRING解析获得,为了避免输入参数中附加了不必要的参数,所以使用*args, **kwargs的结构将多余的参数存储并忽略掉

以避免多余的参数使函数因参数数量不符造成报错

 

每个函数都增加*args, **kwargs的参数显得多余,同时发现,每个函数的必须参数在函数定义的参数列表中已经可以体现,

具体思路是:

使用 inspect.getargspec(func)获得参数的具体参数列表

则其中的args.defaults即为有默认值参数的默认值,使用len(args.defaults)获取有默认值参数的个数

那么args.args[:-len(args.defaults)]就是必须参数的列表,因为在定义中,有默认值的参数必须在无默认值参数的后面

进一步,如果没有args.varargs和not args.keywords即可变参数,则将所有多余的参数过滤

同时经过统一格式化的参数名,也可以直接映射到相应的路径

那么在改进之后的代码则如下

def route(path="", required=None):
    def route_func(func):
        def params_check_func(params):
            args = inspect.getargspec(func)
            var_required = required
            if not var_required:
                var_required = [arg for arg in args.args[:-len(args.defaults)]]
            for required_param in (var_required if var_required else []):
                if required_param not in params:
                    raise ParamsError("%s is required" % required_param)
            if not args.varargs and not args.keywords:
                for param_key in params.keys():
                    if param_key not in args.args:
                        params.pop(param_key)
            return func(**params)
        global functions
        var_path = path
        if not var_path:
            var_path = "/%s/" % func.func_name.replace("__", "/")
        functions[var_path] = params_check_func
        return params_check_func
    return route_func

@route()
def foo__bar(p1, p2=1):
    return do_sth(p1, p2)

 

posted @ 2017-04-10 08:43  ruizhang3  阅读(430)  评论(0编辑  收藏  举报