zoukankan      html  css  js  c++  java
  • Python flask-restful框架讲解

    Flask-RESTful 是一个 Flask 扩展,它添加了快速构建 REST APIs 的支持。它当然也是一个能够跟你现有的ORM/库协同工作的轻量级的扩展。Flask-RESTful 鼓励以最小设置的最佳实践。如果你熟悉 Flask 的话,Flask-RESTful 应该很容易上手。
    关于flask的使用,参考我的之前的博客:https://blog.csdn.net/shifengboy/article/details/114274271

    flask-restful官方文档:https://flask-restful.readthedocs.io/en/lates
    中文文档:http://www.pythondoc.com/Flask-RESTful/

    flask-restful 安装

    pip install flask-restful
    

    flask-restful使用

    简单上手

    from flask import Flask
    from flask_restful import Resource, Api
    
    app = Flask(__name__)
    api = Api(app)
    
    class HelloWorld(Resource):
        def get(self):
            return {'hello': 'world'}
    
    api.add_resource(HelloWorld, '/')
    
    if __name__ == '__main__':
        app.run(debug=True)
    

    运行结果:

    $ curl http://127.0.0.1:5000/
    {"hello": "world"}
    

    Resourceful 路由

    Flask-RESTful 提供的主要构建块是资源。资源构建在 Flask 可插入视图之上,只需在资源上定义方法,就可以轻松访问多个 HTTP 方法。一个 todo 应用程序的基本 CRUD 资源是这样的:

    from flask import Flask, request
    from flask_restful import Resource, Api
    
    app = Flask(__name__)
    api = Api(app)
    
    todos = {}
    
    class TodoSimple(Resource):
        def get(self, todo_id):
            return {todo_id: todos[todo_id]}
    
        def put(self, todo_id):
            todos[todo_id] = request.form['data']
            return {todo_id: todos[todo_id]}
    
    api.add_resource(TodoSimple, '/<string:todo_id>')
    
    if __name__ == '__main__':
        app.run(debug=True)
    

    运行结果:

    chenshifengdeMacBook-Pro:~ chenshifeng$ curl http://localhost:5000/todo1 -d "data=Remember the milk" -X PUT
    {
        "todo1": "Remember the milk"
    }
    chenshifengdeMacBook-Pro:~ chenshifeng$ curl http://localhost:5000/todo1
    {
        "todo1": "Remember the milk"
    }
    chenshifengdeMacBook-Pro:~ chenshifeng$ curl http://localhost:5000/todo2 -d "data=Change my brakepads" -X PUT
    {
        "todo2": "Change my brakepads"
    }
    chenshifengdeMacBook-Pro:~ chenshifeng$ curl http://localhost:5000/todo2
    {
        "todo2": "Change my brakepads"
    }
    chenshifengdeMacBook-Pro:~ chenshifeng$ 
    

    Restful 能够从 view 方法中理解多种返回值。类似于 Flask,你可以返回任何可迭代的并且它将被转换成一个响应,包括原始 Flask 响应对象。还支持使用多个返回值设置响应代码和响应头,如下所示:

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
    """
    @author:chenshifeng
    @file:flask_restful_demo.py
    @time:2021/03/05
    """
    from flask import Flask, request
    from flask_restful import Resource, Api
    
    app = Flask(__name__)
    api = Api(app)
    
    class Todo1(Resource):
        def get(self):
            # Default to 200 OK
            return {'task': 'Hello world'}
    
    class Todo2(Resource):
        def get(self):
            # Set the response code to 201
            return {'task': 'Hello world'}, 201
    
    class Todo3(Resource):
        def get(self):
            # Set the response code to 201 and return custom headers
            return {'task': 'Hello world'}, 201, {'Etag': 'some-opaque-string'}
    
    api.add_resource(Todo1,'/todo1')
    api.add_resource(Todo2,'/todo2')
    api.add_resource(Todo3,'/todo3')
    
    if __name__ == '__main__':
        app.run(debug=True)
    

    运行结果:

    chenshifengdeMacBook-Pro:~ chenshifeng$ curl -i  http://127.0.0.1:5000/todo1 
    HTTP/1.0 200 OK
    Content-Type: application/json
    Content-Length: 30
    Server: Werkzeug/1.0.1 Python/3.9.2
    Date: Fri, 05 Mar 2021 16:08:28 GMT
    
    {
        "task": "Hello world"
    }
    chenshifengdeMacBook-Pro:~ chenshifeng$ curl -i  http://127.0.0.1:5000/todo2
    HTTP/1.0 201 CREATED
    Content-Type: application/json
    Content-Length: 30
    Server: Werkzeug/1.0.1 Python/3.9.2
    Date: Fri, 05 Mar 2021 16:08:32 GMT
    
    {
        "task": "Hello world"
    }
    chenshifengdeMacBook-Pro:~ chenshifeng$ curl -i  http://127.0.0.1:5000/todo3
    HTTP/1.0 201 CREATED
    Content-Type: application/json
    Content-Length: 30
    Etag: some-opaque-string
    Server: Werkzeug/1.0.1 Python/3.9.2
    Date: Fri, 05 Mar 2021 16:08:34 GMT
    
    {
        "task": "Hello world"
    }
    chenshifengdeMacBook-Pro:~ chenshifeng$ 
    
    

    Endpoints 端点

    很多时候,在一个 API 中,你的资源会有多个 url。可以将多个 url 传递给 Api 对象上的 add _ resource ()方法。每一个都将被路由到Resource

    api.add_resource(HelloWorld,
        '/',
        '/hello')
    

    您还可以将路径的某些部分作为变量匹配到Resource。

    api.add_resource(Todo,
        '/todo/<int:todo_id>', endpoint='todo_ep')
    

    演示代码:

    from flask import Flask
    from flask_restful import Resource, Api
    
    app = Flask(__name__)
    api = Api(app)
    
    
    class HelloWorld(Resource):
        def get(self):
            return {'hello': 'world'}
    
    
    class Todo(Resource):
        def get(self, todo_id):
            # Default to 200 OK
            return {'task': 'Hello world'}
    
    
    api.add_resource(HelloWorld, '/', '/hello')
    api.add_resource(Todo, '/todo/<int:todo_id>', endpoint='todo_ep')
    
    if __name__ == '__main__':
        app.run(debug=True)
    

    演示结果:

    chenshifengdeMacBook-Pro:~ chenshifeng$ curl  http://127.0.0.1:5000/
    {
        "hello": "world"
    }
    chenshifengdeMacBook-Pro:~ chenshifeng$ curl  http://127.0.0.1:5000/hello
    {
        "hello": "world"
    }
    chenshifengdeMacBook-Pro:~ chenshifeng$ curl  http://127.0.0.1:5000/todo/1
    {
        "task": "Hello world"
    }
    chenshifengdeMacBook-Pro:~ chenshifeng$ curl  http://127.0.0.1:5000/todo/2
    {
        "task": "Hello world"
    }
    

    参数解析

    虽然 Flask 可以方便地访问请求数据(即 querystring 或 POST 表单编码的数据) ,但验证表单数据仍然是一件痛苦的事情。使用类似于 argparse 的库对请求数据验证提供内置支持。

    from flask import Flask
    from flask_restful import reqparse, Api, Resource
    
    app = Flask(__name__)
    api = Api(app)
    
    parser = reqparse.RequestParser()
    parser.add_argument('rate', type=int, help='Rate to charge for this resource')
    
    class Todo(Resource):
        def post(self):
            args = parser.parse_args()
            print(args)
            # Default to 200 OK
            return {'task': 'Hello world'}
    
    
    api.add_resource(Todo,'/todos' )
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    chenshifengdeMacBook-Pro:~ chenshifeng$ curl -d 'rate=100' http://127.0.0.1:5000/todos
    {
        "task": "Hello world"
    }
    chenshifengdeMacBook-Pro:~ chenshifeng$ curl -d 'rate=foo' http://127.0.0.1:5000/todos
    {
        "message": {
            "rate": "Rate to charge for this resource"
        }
    }
    

    与 argparse 模块不同,reqparse. RequestParser.parse _ args ()返回 Python 字典,而不是自定义数据结构。

    输入模块提供了许多常用的转换函数,例如 inputs.date ()和 inputs.url ()。
    使用 strict = True 调用 parse _ args 可以确保在请求包含您的解析器没有定义的参数时抛出错误。

    args = parser.parse_args(strict=True)
    
    $ curl -d 'rate2=foo' http://127.0.0.1:5000/todos
    {
        "message": "Unknown arguments: rate2"
    }
    

    数据格式化

    默认情况下,在你的返回迭代中所有字段将会原样呈现。尽管当你刚刚处理 Python 数据结构的时候,觉得这是一个伟大的工作,但是当实际处理它们的时候,会觉得十分沮丧和枯燥。为了解决这个问题,Flask-RESTful 提供了 fields 模块和 marshal_with() 装饰器。类似 Django ORM 和 WTForm,你可以使用 fields 模块来在你的响应中格式化结构。

    from flask import Flask
    from flask_restful import fields, marshal_with, Resource, Api
    
    app = Flask(__name__)
    api = Api(app)
    
    resource_fields = {
        'task':   fields.String,
        'uri':    fields.Url('todo')
    }
    
    class TodoDao(object):
        def __init__(self, todo_id, task):
            self.todo_id = todo_id
            self.task = task
    
            # This field will not be sent in the response
            self.status = 'active'
    
    class Todo(Resource):
        @marshal_with(resource_fields)
        def get(self, **kwargs):
            return TodoDao(todo_id='my_todo', task='Remember the milk')
    
    api.add_resource(Todo,'/todo')
    
    if __name__ == '__main__':
        app.run(debug=True)
    

    上面的例子接受一个 python 对象并准备将其序列化。marshal_with() 装饰器将会应用到由 resource_fields 描述的转换。从对象中提取的唯一字段是 task。fields.Url 域是一个特殊的域,它接受端点(endpoint)名称作为参数并且在响应中为该端点生成一个 URL。许多你需要的字段类型都已经包含在内。请参阅 fields 指南获取一个完整的列表。

    $ curl  http://127.0.0.1:5000/todo
    {
        "task": "Remember the milk",
        "uri": "/todo"
    }
    

    完整例子

    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)
    

    获取列表

    $ curl http://localhost:5000/todos
    {
        "todo1": {
            "task": "build an API"
        },
        "todo2": {
            "task": "?????"
        },
        "todo3": {
            "task": "profit!"
        }
    }
    

    获取一个单独的任务

    $ curl http://localhost:5000/todos/todo3
    {
        "task": "profit!"
    }
    

    删除一个任务

    $ curl http://localhost:5000/todos/todo2 -X DELETE -v
    *   Trying ::1...
    * TCP_NODELAY set
    * Connection failed
    * connect to ::1 port 5000 failed: Connection refused
    *   Trying 127.0.0.1...
    * TCP_NODELAY set
    * Connected to localhost (127.0.0.1) port 5000 (#0)
    > DELETE /todos/todo2 HTTP/1.1
    > Host: localhost:5000
    > User-Agent: curl/7.64.1
    > Accept: */*
    > 
    * HTTP 1.0, assume close after body
    < HTTP/1.0 204 NO CONTENT
    < Content-Type: application/json
    < Server: Werkzeug/1.0.1 Python/3.9.2
    < Date: Sat, 06 Mar 2021 03:29:33 GMT
    < 
    * Closing connection 0
    

    增加一个新的任务

    $ curl http://localhost:5000/todos -d "task=something new" -X POST -v
    Note: Unnecessary use of -X or --request, POST is already inferred.
    *   Trying ::1...
    * TCP_NODELAY set
    * Connection failed
    * connect to ::1 port 5000 failed: Connection refused
    *   Trying 127.0.0.1...
    * TCP_NODELAY set
    * Connected to localhost (127.0.0.1) port 5000 (#0)
    > POST /todos HTTP/1.1
    > Host: localhost:5000
    > User-Agent: curl/7.64.1
    > Accept: */*
    > Content-Length: 18
    > Content-Type: application/x-www-form-urlencoded
    > 
    * upload completely sent off: 18 out of 18 bytes
    * HTTP 1.0, assume close after body
    < HTTP/1.0 201 CREATED
    < Content-Type: application/json
    < Content-Length: 32
    < Server: Werkzeug/1.0.1 Python/3.9.2
    < Date: Sat, 06 Mar 2021 03:31:02 GMT
    < 
    {
        "task": "something new"
    }
    * Closing connection 0
    

    更新一个任务

    $  curl http://localhost:5000/todos/todo3 -d "task=something different" -X PUT -v
    *   Trying ::1...
    * TCP_NODELAY set
    * Connection failed
    * connect to ::1 port 5000 failed: Connection refused
    *   Trying 127.0.0.1...
    * TCP_NODELAY set
    * Connected to localhost (127.0.0.1) port 5000 (#0)
    > PUT /todos/todo3 HTTP/1.1
    > Host: localhost:5000
    > User-Agent: curl/7.64.1
    > Accept: */*
    > Content-Length: 24
    > Content-Type: application/x-www-form-urlencoded
    > 
    * upload completely sent off: 24 out of 24 bytes
    * HTTP 1.0, assume close after body
    < HTTP/1.0 201 CREATED
    < Content-Type: application/json
    < Content-Length: 38
    < Server: Werkzeug/1.0.1 Python/3.9.2
    < Date: Sat, 06 Mar 2021 03:32:44 GMT
    < 
    {
        "task": "something different"
    }
    * Closing connection 0
    

    获取最新列表

    $ curl http://localhost:5000/todos
    {
        "todo1": {
            "task": "build an API"
        },
        "todo3": {
            "task": "something different"
        },
        "todo4": {
            "task": "something new"
        }
    }
    
    如果您觉得本篇文章还不错,欢迎点赞,转发分享(转发请注明出处),感谢~~
  • 相关阅读:
    slot 的简单使用(一)匿名插槽
    修改Tooltip 文字提示 的背景色 箭头颜色
    解决vue/cli3.0 语法验证规则 ESLint: Expected indentation of 2 spaces but found 4. (indent)
    洛谷P2014 选课(树形DP+分组背包)
    洛谷P4316 绿豆蛙的归宿(概率DP/期望DP+拓扑排序)
    Atcoder Beginner Contest 144 F- Fork the Road(概率DP/期望DP)
    Atcoder ABC144 Gluttony(贪心+二分)
    洛谷P1352 没有上司的舞会(树形DP+记忆化)
    HDU2476 String painter(区间DP)
    POJ1651 Multiplication Puzzle(区间DP+记忆化搜索)
  • 原文地址:https://www.cnblogs.com/feng0815/p/14490087.html
Copyright © 2011-2022 走看看