还是选择放弃使用 Eve 作为 RESTful 框架的选择

虽然,Eve 框架很强大,也是基于 Flask,但是我还是选择放弃使用 Eve 了。几点原因:

  1. Eve 内置直接支持的数据库是 Mongo,我们已经不准备在 Mongo 上投入资源了。
  2. Eve 对额外的数据库的支持需要通过 eve-sqlalchemy,来调用 sqlalchemy 和 flask 等,这样就把问题搞的有点复杂化了。等于 flask、sqlalchemy、flask-sqlalchemy、eve、eve-sqllchemy,组合在一起,才能通过 orm 来支持各类数据库。
  3. Eve 的使用还是非常复杂的。
  4. 比较下来 flask-restful 框架就足够了。

flask-restful 框架通过将 class 直接转换为 RESTful 接口:

# Todo
# shows a single todo item and lets you delete a todo item
class Todo(Resource):
    def get(self, todo_id):
        abort_if_todo_doesnt_exist(todo_id)
        return TODOS[todo_id]

    def delete(self, todo_id):
        abort_if_todo_doesnt_exist(todo_id)
        del TODOS[todo_id]
        return '', 204

    def put(self, todo_id):
        args = parser.parse_args()
        task = {'task': args['task']}
        TODOS[todo_id] = task
        return task, 201

用类似下面的方法就直接 ok 了

##
## Actually setup the Api resource routing here
##
api.add_resource(TodoList, '/todos')
api.add_resource(Todo, '/todos/<todo_id>')

对于上面 Todo 这个 class 中数据怎么来,这不是 flask-restful 关心的,我们可以使用 flask-sqlalchemy 来完成这些和数据库操作的真正业务逻辑。

我一直觉得选择 flask 是正确的,它在很多理念的设计上也像微服务框架,每个人做好自己的事情,互相有一个约定来达到协同。

从 git 仓库看,flask-restful 的更新也还不错。

当然用了这个的话,Eve 框架很多强大的比如根据配置文件就能生成接口的能力,就要自己想办法了,初步考虑因为整个这些探索和设计都是为了自动根据数据库来生成 RESTful 接口程序,所以可以使用 DSL 来描述接口要求,将 DSL 转换为基于 flask 体系的 python 程序,来达到这个目的。

Flask, Sanic and Eve, Three Good Web Frameworks For Python

Prepare to improve my English writing ability. I know my English is very stiff, grammar also has many problems. But I still try to persist it. Thanks and sorry for my poor English

In recent years, the performance of Python has been greatly improved. Base on highly asynchronous event processing ability, web framework of Python have been greatly improved. After continued follow-up.  I think the following frameworks are worth future study.

In normal working hours, We use Flask as a web service framework. So I have some preference for flask-like type.

  1. Flask. http://flask.pocoo.org/  We all know, flask has long history and has stable performance. It’s a micro framework and has a lots of extensions. So it’s lightweight and configurable. I believe we will continue to use flask as the mainly web service framework for a long time. The latest version is 0.12.2.
  2. Sanic. https://github.com/channelcat/sanic This is a magical tool, it’s improve the web service performance by using asynchronous technology by Python 3.5+. In practical application, I think flask is enough. Flask can easily do hundreds concurrent in basic server on the cloud. But I believe it, The performance improvement is endless. In particular, the introduction of more and more Micro-Services framework requires that web service framework has strong ability. Sanic web framework much like flask, therefore, the learning and migration costs are relatively low.The latest version is 0.6.0.
  3. Eve. https://github.com/pyeve/eve Eve framework is based on flask, and mainly target is Restful API. As mentioned above, Restful API based design has became the standard in today’s internet application. Eve framework through the configuration file to quickly setup the restful api program. Design of Eve is very smart, we can learn a lots of python programming skill from it.

Currently, based on Flask, I’m studying Sanic and Eve, first, improve program skill of web service. Second, improve my English. At last, python is a profound script language, we will study lots from these famous frameworks, and I will continue share my study notes here. Looking forward to everyone’s correction.

python flask 写 api 如何返回自定义错误

在 python 开发中,利用 flask 写 restful api 函数的时候,除了标准的400、500等这些返回码通过 abort() 返回以外,怎么另外返回自定义的错误代码和信息呢?

我们碰到的业务场景是对于api 输入参数的各类校验以及在业务逻辑执行的时候,都会返回统一的400代码,同时也会返回我们约定的描述详细错误的代码以及描述字符串,提供给调用方来处理,这样可以让其用户体验做得更好,同时详细错误代码和描述字符串也会自动打印在 log 日志中。

flask 的官方文档中告诉我们:

默认情况下,错误代码会显示一个黑白的错误页面。如果你要定制错误页面, 可以使用 errorhandler() 装饰器

在写 restful api 的时候,并没有页面可以返回,我们可以在 flask 提供的代码基础上稍加改造如下。

在你的初始化 flask app 的相关代码中加入下面两个函数:

@app.errorhandler(CustomFlaskErr)
def handle_flask_error(error):

    # response 的 json 内容为自定义错误代码和错误信息
    response = jsonify(error.to_dict())

    # response 返回 error 发生时定义的标准错误代码
    response.status_code = error.status_code

    return response
class CustomFlaskErr(Exception):

    # 默认的返回码
    status_code = 400

    # 自己定义了一个 return_code,作为更细颗粒度的错误代码
    def __init__(self, return_code=None, status_code=None, payload=None):
        Exception.__init__(self)
        self.return_code = return_code
        if status_code is not None:
            self.status_code = status_code
        self.payload = payload

    # 构造要返回的错误代码和错误信息的 dict
    def to_dict(self):
        rv = dict(self.payload or ())

        # 增加 dict key: return code
        rv['return_code'] = self.return_code

        # 增加 dict key: message, 具体内容由常量定义文件中通过 return_code 转化而来
        rv['message'] = J_MSG[self.return_code]

        # 日志打印
        logger.warning(J_MSG[self.return_code])

        return rv

CustomFlaskErr 是我们自己写的处理错误的类,然后通过 @app.errorhandler(CustomFlaskErr) 这个装饰器在 flask 中注册。

具体功能在注释里基本都写了,我们看一下怎么使用这个自定义错误处理器。

# 用户名输入为空
if user_name is None:
    raise CustomFlaskErr(USER_NAME_ILLEGAL, status_code=400)

当需要处理某个错误的时候,rasie 刚才的 CustomFlaskErr,传递另外定义好的自己的错误代码,以及标准的返回代码;

上面说的常量定义文件可以参考如下:

USER_ALREADY_EXISTS = 20001  # 用户已经存在
J_MSG = {USER_ALREADY_EXISTS: 'user already exists'}

通过这样的机制,就做到了在具体 restful api 的业务逻辑代码中简单的进行各类自定义错误的处理,所有的错误处理是集中的,细颗粒度的错误代码和消息也是集中维护,便于扩展。

flask 官方文档和一些网上的资料都说比较简单,实践中摸索了这样的实现方式供参考。

python 中使用装饰器来统一检查 flask 用户权限

最近在一个项目中,需要判断 restful 接口函数传入的时候,是否之前已经登录状态是某个特定用户,以及该用户有没有指定的权限。检查下来如果没有的话,立刻返回错误,中断功能。

遮掩的场景虽然也可以通过标准的调用函数来操作,但都不如用装饰器来得简单。都知道装饰器好用不好写,废话不说,先来看看这个场景怎么实现,还是有一定的通用性的。

def validate_current_is_admin(f):
    @functools.wraps(f)
    def decorated_function(*args, **kws):
        # 需要在登录状态调用, 检查是否为有admin权限的用户登录,
        # 如果不是,返回错误码;
        if g.user.user_name != 'admin':
            raise CustomFlaskErr(USER_MUST_HAS_ADMIN_PRIVILEGE, status_code=401)

        # 验证权限是否为 admin, 不是的话,返回401错误
        if g.user.role_id != Permission.ADMIN:
            raise CustomFlaskErr(USER_MUST_HAS_ADMIN_PRIVILEGE, status_code=401)

        return f(*args, **kws)

    return decorated_function

这是一个标准的装饰器的写法,如果你要写一个简单的装饰器,整个框架可以参考。

装饰器调用举例:

@app.route('/api/create_user', methods=['POST'])
@auth.login_required
@validate_current_is_admin
def create_user():

    # 获得参数
    user_name = request.json.get('user_name')
    password = request.json.get('password')
......

 

核心代码的业务逻辑也不复杂,根据 flask 的 g 对象中预存的用户 user 进行检查处理,flask 的这些定义非常灵活,flask.g 怎么使用可以查看 flask 的文档。

这里的 user 以及相关的属性属于具体业务逻辑,就不展开解释了,可以望文生义。

因为不对 args 和 kws 这些参数进行解析和处理,所处理的是 flask 全局对象。最后将参数都原路打包返回即可,没有问题的话交给使用装饰器的代码继续处理。

这个例子比较简单,主要还是熟悉装饰器的基本用法。

学习 python 这一年

大约2015年4月到5月开始,正式学习 python,发现之前大概在2009年左右也学过,可能当时没有坚持下去。

记得去年开始学 python 的时候,1个月左右,给同事写了一个基于 csv 处理的小程序,当时就被 python 的简洁优雅所倾倒。

然后边学边写东西,本来为公司 zion 框架写一个 dsl,当时水平还不行,所以写成了一个 sql 的包装工具 r2,用来在前端业务逻辑调用后台数据库的时候简化 sql 语句写法以及做到和数据库无关。

真正让我自己 python 水平感觉有点突破的是几个事情:

1 写真正生产上的应用。需要考虑的事情非常多,比如一个简单的 r2,就让我学到了 log、conf、import、class 等很多基本知识的应用,以及单元测试、性能测试。

2 教别人 python。这是让自己的基础知识巩固最好的办法之一。要把别人教会,且处理各类奇怪的运行错误,很有挑战。这样的结果,是对诸如列表、字符串、元组、字典、函数、循环等基本概念非常清楚。不管多复杂的程序,90%的代码其实还是基本的操作,在这90%的代码设计和编写的时候可以减少错误,节约时间,自然有用。

3 看资料。晚上有大量的英文和中文的 python 资料,绝大多数都写的很好,受益匪浅。

在我的推进下,公司也在个别项目上开始使用 python,通过 flask 来构建 restful 接口,机器学习也基于 keras 和 tensorflow 开始了实际的应用。

我相信,进步会是跳跃式的,而这还是仅仅发生在一年间,python 在胶水语言的应用,连接 js、php、java 和 moble 开发上面,以及 python 在人工智能、机器学习上这几年强劲的表现,都让人激动不已。