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 响应对象。还支持使用多个返回值设置响应代码和响应头,如下所示:
| |
| |
| """ |
| @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): |
| |
| return {'task': 'Hello world'} |
| |
| class Todo2(Resource): |
| def get(self): |
| |
| return {'task': 'Hello world'}, 201 |
| |
| class Todo3(Resource): |
| def get(self): |
| |
| 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): |
| |
| 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) |
| |
| 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 |
| |
| |
| 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') |
| |
| |
| |
| |
| 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 |
| |
| |
| |
| |
| 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 |
| |
| |
| |
| |
| 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 ( |
| > 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 ( |
| > 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 ( |
| > 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" |
| } |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】