python实现RESTful服务(基于flask)

前言

上一篇文章讲到 如何用java实现RESTful服务 ,这就来讲讲怎么用python来实现吧,因为要搭建一套java和python互调的服务,两者都不能少啊。
对于python的述求呢也是要轻量化,并且能快捷开发,因为我这套服务主要控制逻辑还是在java端,python这块负责一些工具逻辑,比如网页爬取、文字转拼音、图像处理等等,在java端当成工具类来使用,所以轻量和快捷开发就是重点了

web框架选择

java端需要选择一个web容器,同样的,python也需要选择一个web框架,当然自己实现一套也可以哈,不过本着【人生苦短,我用python】的原则,还是找框架吧

知乎上有一个讨论 Python 有哪些好的 Web 框架? ,从这个讨论中最后我选择了flask,原因是:

  • Django,流行但是笨重,还麻烦,人生苦短,肯定不选
  • web.py,轻量,但据说作者仙逝无人维护,好吧,先pass
  • tornado,据说倡导自己造轮子,虽然是facebook开源的吧,但听到这个,就算了吧
  • 还有一些其他的就不说了,直到看到一个人做了如下回复,就暂定了flask,再发现flask写RESTful居然超简单,于是就它了

    from flask import Flask # Flask初始化参数尽量使用你的包名,这个初始化方式是官方推荐的,官方解释:http://flask.pocoo.org/docs/0.12/api/#flask.Flask app = Flask(__name__) @app.route('/HelloWorld') def hello_world(): return "Hello World!" if __name__ == "__main__": # 这种是不太推荐的启动方式,我这只是做演示用,官方启动方式参见:http://flask.pocoo.org/docs/0.12/quickstart/#a-minimal-application app.run(debug=True)

    注:代码里参见的官方文档都是英文的,也有一个不错的中文网站做了很好的翻译,可以在扎个上面找到很好的对应:
    http://www.pythondoc.com/flask/quickstart.html

    验证结果:

    PostMan验证

    光实现一个简单网页可不是本事(毕竟python用SimpleHTTPServer都可以不用写代码),实现RESTful是不是也同样简单呢?

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # by vellhe 2017/7/9
    from flask import Flask, abort, request, jsonify
    app = Flask(__name__)
    # 测试数据暂时存放
    tasks = []
    @app.route('/add_task/', methods=['POST'])
    def add_task():
        if not request.json or 'id' not in request.json or 'info' not in request.json:
            abort(400)
        task = {
            'id': request.json['id'],
            'info': request.json['info']
        tasks.append(task)
        return jsonify({'result': 'success'})
    @app.route('/get_task/', methods=['GET'])
    def get_task():
        if not request.args or 'id' not in request.args:
            # 没有指定id则返回全部
            return jsonify(tasks)
        else:
            task_id = request.args['id']
            task = filter(lambda t: t['id'] == int(task_id), tasks)
            return jsonify(task) if task else jsonify({'result': 'not found'})
    if __name__ == "__main__":
        # 将host设置为0.0.0.0,则外网用户也可以访问到这个服务
        app.run(host="0.0.0.0", port=8383, debug=True)
    
    get_task by id

    以上是通过最原始的方式实现,没有使用flask的RESTful扩展库,就这种实现方式都已经非常给力了,这么一点代码就搞定了RESTful服务,真的是人生苦短啊,但这真的就完美了吗?
    答案肯定是no啦~ 必须要试试flask的RESTful扩展库才知道什么叫真正的爽啊

    使用flask的RESTful扩展库 flask-restful

    强烈建议参考官方文档(因为下面我也是照搬不误):http://flask-restful.readthedocs.io/en/0.3.5/quickstart.html#a-minimal-api

    安装flask-restful
    pip install flask-restful
    
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # by vellhe 2017/7/9
    from flask import Flask
    from flask_restful import reqparse, abort, Api, Resource
    app = Flask(__name__)
    api = Api(app)
    TODOS = {
        'todo1': {'task': 'build an API'},
        'todo2': {'task': '哈哈哈'},
        'todo3': {'task': 'profit!'},
    def abort_if_todo_doesnt_exist(todo_id):
        if todo_id not in TODOS:
            abort(404, message="Todo {} doesn't exist".format(todo_id))
    parser = reqparse.RequestParser()
    parser.add_argument('task')
    # 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
    # TodoList
    # shows a list of all todos, and lets you POST to add new tasks
    class TodoList(Resource):
        def get(self):
            return TODOS
        def post(self):
            args = parser.parse_args()
            todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1
            todo_id = 'todo%i' % todo_id
            TODOS[todo_id] = {'task': args['task']}
            return TODOS[todo_id], 201
    ## Actually setup the Api resource routing here
    api.add_resource(TodoList, '/todos')
    api.add_resource(Todo, '/todos/<todo_id>')
    if __name__ == '__main__':
        app.run(debug=True)
    

    查询列表: