Flask-RESTful

前面,我们介绍了 REST Web 服务,并使用 Flask 提供服务。这里,我们使用第三方库 Flask-RESTful,它使得在 Flask 中提供 REST 服务变得更加简单。

安装

使用 pip 安装:

  1. $ pip install flask-restful

使用

下面我们主要使用官方文档的例子进行说明。

Hello World

我们先来看一个简单的例子。

  1. # -*- coding: utf-8 -*-
  2. from flask import Flask
  3. from flask_restful import Resource, Api
  4. app = Flask(__name__)
  5. api = Api(app)
  6. class HelloWorld(Resource):
  7. def get(self):
  8. return {'hello': 'world'}
  9. api.add_resource(HelloWorld, '/')
  10. if __name__ == '__main__':
  11. app.run(debug=True)

上面的代码应该不难看懂。我们定义了一个 HelloWorld 的类,该类继承自 Resource,在类里面,我们定义了 get 方法,该方法跟 HTTP 请求中的 GET 方法对应。接着,我们使用 add_resource() 方法添加资源,该方法的第 1 个参数就是我们定义的类,第 2 个参数是 URL 路由。

将上面的代码保存为 app.py,在终端运行:

  1. $ python app.py
  2. * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
  3. * Restarting with stat
  4. * Debugger is active!
  5. * Debugger pin code: 109-566-036

使用 curl 测试,如下:

  1. $ curl http://127.0.0.1:5000/
  2. {
  3. "hello": "world"
  4. }

另外,add_resource() 方法也支持添加多个路由,比如:

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

这样访问 http://127.0.0.1:5000/http://127.0.0.1:5000/hello 效果是一样的。

带参数的请求

Flask-RESTful 也支持路由带参数的请求,跟 Flask 的实现是类似的,看下面这个例子。

  1. # -*- coding: utf-8 -*-
  2. from flask import Flask, request
  3. from flask_restful import Resource, Api
  4. app = Flask(__name__)
  5. api = Api(app)
  6. todos = {}
  7. class TodoSimple(Resource):
  8. def get(self, todo_id):
  9. return {todo_id: todos[todo_id]}
  10. def put(self, todo_id):
  11. todos[todo_id] = request.form['data']
  12. return {todo_id: todos[todo_id]}
  13. # 路由带参数
  14. api.add_resource(TodoSimple, '/<string:todo_id>')
  15. if __name__ == '__main__':
  16. app.run(debug=True)

使用 curl 测试,如下:

  1. $ curl -X PUT http://localhost:5000/todo1 -d "data=shopping"
  2. {
  3. "todo1": "shopping"
  4. }
  5. $ curl -X GET http://localhost:5000/todo1
  6. {
  7. "todo1": "shopping"
  8. }

参数解析

Flask-RESTful 提供了 reqparse 库来简化我们访问并验证表单的工作,将上面的代码使用 reqparse 改写,如下:

  1. # -*- coding: utf-8 -*-
  2. from flask import Flask, request
  3. from flask_restful import Resource, Api, reqparse
  4. app = Flask(__name__)
  5. api = Api(app)
  6. parser = reqparse.RequestParser()
  7. parser.add_argument('task', type=str) # 相当于添加 form 表单字段,并给出类型
  8. todos = {}
  9. class TodoSimple(Resource):
  10. def get(self, todo_id):
  11. return {todo_id: todos[todo_id]}
  12. def put(self, todo_id):
  13. args = parser.parse_args() # 获取表单的内容, args 是一个字典
  14. if args['task']:
  15. todos[todo_id] = args['task']
  16. else:
  17. return 'error!'
  18. return {todo_id: todos[todo_id]}
  19. api.add_resource(TodoSimple, '/<string:todo_id>')
  20. if __name__ == '__main__':
  21. app.run(debug=True)

上面的代码中,我们使用 add_argument() 方法添加 form 表单字段,并指定其类型。获取表单内容使用 parse_args() 方法,该方法返回一个字典,字典的 key 就是表单的字段。

使用 curl 测试,如下:

  1. $ curl -X PUT http://localhost:5000/todo1 -d "task=shopping"
  2. {
  3. "todo1": "shopping"
  4. }

一个完整的例子

下面这个完整的例子来自于官方文档

  1. from flask import Flask
  2. from flask_restful import reqparse, abort, Api, Resource
  3. app = Flask(__name__)
  4. api = Api(app)
  5. TODOS = {
  6. 'todo1': {'task': 'build an API'},
  7. 'todo2': {'task': '?????'},
  8. 'todo3': {'task': 'profit!'},
  9. }
  10. def abort_if_todo_doesnt_exist(todo_id):
  11. if todo_id not in TODOS:
  12. abort(404, message="Todo {} doesn't exist".format(todo_id))
  13. parser = reqparse.RequestParser()
  14. parser.add_argument('task')
  15. # Todo
  16. # shows a single todo item and lets you delete a todo item
  17. class Todo(Resource):
  18. def get(self, todo_id):
  19. abort_if_todo_doesnt_exist(todo_id)
  20. return TODOS[todo_id]
  21. def delete(self, todo_id):
  22. abort_if_todo_doesnt_exist(todo_id)
  23. del TODOS[todo_id]
  24. return '', 204
  25. def put(self, todo_id):
  26. args = parser.parse_args()
  27. task = {'task': args['task']}
  28. TODOS[todo_id] = task
  29. return task, 201
  30. # TodoList
  31. # shows a list of all todos, and lets you POST to add new tasks
  32. class TodoList(Resource):
  33. def get(self):
  34. return TODOS
  35. def post(self):
  36. args = parser.parse_args()
  37. todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1
  38. todo_id = 'todo%i' % todo_id
  39. TODOS[todo_id] = {'task': args['task']}
  40. return TODOS[todo_id], 201
  41. ## Actually setup the Api resource routing here
  42. api.add_resource(TodoList, '/todos')
  43. api.add_resource(Todo, '/todos/<todo_id>')
  44. if __name__ == '__main__':
  45. app.run(debug=True)

运行上面的代码,然后使用 curl 进行测试,如下:

  1. # 获取所有 todos
  2. $ curl http://localhost:5000/todos
  3. {
  4. "todo1": {
  5. "task": "build an API"
  6. },
  7. "todo2": {
  8. "task": "?????"
  9. },
  10. "todo3": {
  11. "task": "profit!"
  12. }
  13. }
  14. # 获取单个 todo
  15. $ curl http://localhost:5000/todos/todo2
  16. {
  17. "task": "?????"
  18. }
  19. # 删除某个 todo
  20. $ curl -v -X DELETE http://localhost:5000/todos/todo2
  21. * Trying 127.0.0.1...
  22. * Connected to localhost (127.0.0.1) port 5000 (#0)
  23. > DELETE /todos/todo2 HTTP/1.1
  24. > Host: localhost:5000
  25. > User-Agent: curl/7.43.0
  26. > Accept: */*
  27. >
  28. * HTTP 1.0, assume close after body
  29. < HTTP/1.0 204 NO CONTENT
  30. < Content-Type: application/json
  31. < Content-Length: 0
  32. < Server: Werkzeug/0.11.11 Python/2.7.11
  33. < Date: Tue, 18 Oct 2016 04:02:17 GMT
  34. <
  35. * Closing connection 0
  36. # 添加 todo
  37. $ curl -v -X POST http://localhost:5000/todos -d "task=eating"
  38. * Trying 127.0.0.1...
  39. * Connected to localhost (127.0.0.1) port 5000 (#0)
  40. > POST /todos HTTP/1.1
  41. > Host: localhost:5000
  42. > User-Agent: curl/7.43.0
  43. > Accept: */*
  44. > Content-Length: 11
  45. > Content-Type: application/x-www-form-urlencoded
  46. >
  47. * upload completely sent off: 11 out of 11 bytes
  48. * HTTP 1.0, assume close after body
  49. < HTTP/1.0 201 CREATED
  50. < Content-Type: application/json
  51. < Content-Length: 25
  52. < Server: Werkzeug/0.11.11 Python/2.7.11
  53. < Date: Tue, 18 Oct 2016 04:04:16 GMT
  54. <
  55. {
  56. "task": "eating"
  57. }
  58. * Closing connection 0
  59. # 更新 todo
  60. $ curl -v -X PUT http://localhost:5000/todos/todo3 -d "task=running"
  61. * Trying 127.0.0.1...
  62. * Connected to localhost (127.0.0.1) port 5000 (#0)
  63. > PUT /todos/todo3 HTTP/1.1
  64. > Host: localhost:5000
  65. > User-Agent: curl/7.43.0
  66. > Accept: */*
  67. > Content-Length: 12
  68. > Content-Type: application/x-www-form-urlencoded
  69. >
  70. * upload completely sent off: 12 out of 12 bytes
  71. * HTTP 1.0, assume close after body
  72. < HTTP/1.0 201 CREATED
  73. < Content-Type: application/json
  74. < Content-Length: 26
  75. < Server: Werkzeug/0.11.11 Python/2.7.11
  76. < Date: Tue, 18 Oct 2016 04:05:52 GMT
  77. <
  78. {
  79. "task": "running"
  80. }
  81. * Closing connection 0

本文完整的代码在这里

更多阅读