# 使用 Flask-RESTful 設計 RESTful API
前面我已經用 Flask 實現了一個 RESTful 服務器。今天我們將會使用 Flask-RESTful 來實現同一個 RESTful 服務器,Flask-RESTful 是一個可以簡化 APIs 的構建的 Flask 擴展。
## RESTful 服務器
作為一個提醒, 這里就是待完成事項列表 web service 所提供的方法的定義:
========== =============================================== =============================
HTTP 方法 URL 動作
========== =============================================== ==============================
GET http://[hostname]/todo/api/v1.0/tasks 檢索任務列表
GET http://[hostname]/todo/api/v1.0/tasks/[task_id] 檢索某個任務
POST http://[hostname]/todo/api/v1.0/tasks 創建新任務
PUT http://[hostname]/todo/api/v1.0/tasks/[task_id] 更新任務
DELETE http://[hostname]/todo/api/v1.0/tasks/[task_id] 刪除任務
========== ================================================ =============================
這個服務唯一的資源叫做“任務”,它有如下一些屬性:
* **id**: 任務的唯一標識符。數字類型。
* **title**: 簡短的任務描述。字符串類型。
* **description**: 具體的任務描述。文本類型。
* **done**: 任務完成的狀態。布爾值。
## 路由
在上一遍文章中,我使用了 Flask 的視圖函數來定義所有的路由。
Flask-RESTful 提供了一個 Resource 基礎類,它能夠定義一個給定 URL 的一個或者多個 HTTP 方法。例如,定義一個可以使用 HTTP 的 GET, PUT 以及 DELETE 方法的 User 資源,你的代碼可以如下:
from flask import Flask
from flask.ext.restful import Api, Resource
app = Flask(__name__)
api = Api(app)
class UserAPI(Resource):
def get(self, id):
pass
def put(self, id):
pass
def delete(self, id):
pass
api.add_resource(UserAPI, '/users/', endpoint = 'user')
add_resource 函數使用指定的 endpoint 注冊路由到框架上。如果沒有指定 endpoint,Flask-RESTful 會根據類名生成一個,但是有時候有些函數比如 url_for 需要 endpoint,因此我會明確給 endpoint 賦值。
我的待辦事項 API 定義兩個 URLs:/todo/api/v1.0/tasks(獲取所有任務列表),以及 /todo/api/v1.0/tasks/(獲取單個任務)。我們現在需要兩個資源:
class TaskListAPI(Resource):
def get(self):
pass
def post(self):
pass
class TaskAPI(Resource):
def get(self, id):
pass
def put(self, id):
pass
def delete(self, id):
pass
api.add_resource(TaskListAPI, '/todo/api/v1.0/tasks', endpoint = 'tasks')
api.add_resource(TaskAPI, '/todo/api/v1.0/tasks/', endpoint = 'task')
## 解析以及驗證請求
當我在以前的文章中實現此服務器的時候,我自己對請求的數據進行驗證。例如,在之前版本中如何處理 PUT 的:
@app.route('/todo/api/v1.0/tasks/', methods = ['PUT'])
@auth.login_required
def update_task(task_id):
task = filter(lambda t: t['id'] == task_id, tasks)
if len(task) == 0:
abort(404)
if not request.json:
abort(400)
if 'title' in request.json and type(request.json['title']) != unicode:
abort(400)
if 'description' in request.json and type(request.json['description']) is not unicode:
abort(400)
if 'done' in request.json and type(request.json['done']) is not bool:
abort(400)
task[0]['title'] = request.json.get('title', task[0]['title'])
task[0]['description'] = request.json.get('description', task[0]['description'])
task[0]['done'] = request.json.get('done', task[0]['done'])
return jsonify( { 'task': make_public_task(task[0]) } )
在這里, 我必須確保請求中給出的數據在使用之前是有效,這樣使得函數變得又臭又長。
Flask-RESTful 提供了一個更好的方式來處理數據驗證,它叫做 RequestParser 類。這個類工作方式類似命令行解析工具 argparse。
首先,對于每一個資源需要定義參數以及怎樣驗證它們:
from flask.ext.restful import reqparse
class TaskListAPI(Resource):
def __init__(self):
self.reqparse = reqparse.RequestParser()
self.reqparse.add_argument('title', type = str, required = True,
help = 'No task title provided', location = 'json')
self.reqparse.add_argument('description', type = str, default = "", location = 'json')
super(TaskListAPI, self).__init__()
# ...
class TaskAPI(Resource):
def __init__(self):
self.reqparse = reqparse.RequestParser()
self.reqparse.add_argument('title', type = str, location = 'json')
self.reqparse.add_argument('description', type = str, location = 'json')
self.reqparse.add_argument('done', type = bool, location = 'json')
super(TaskAPI, self).__init__()
# ...
在 TaskListAPI 資源中,POST 方法是唯一接收參數的。參數“標題”是必須的,因此我定義一個缺少“標題”的錯誤信息。當客戶端缺少這個參數的時候,Flask-RESTful 將會把這個錯誤信息作為響應發送給客戶端。“描述”字段是可選的,當缺少這個字段的時候,默認的空字符串將會被使用。一個有趣的方面就是 RequestParser 類默認情況下在 request.values 中查找參數,因此 location 可選參數必須被設置以表明請求過來的參數是 request.json 格式的。
TaskAPI 資源的參數處理是同樣的方式,但是有少許不同。PUT 方法需要解析參數,并且這個方法的所有參數都是可選的。
當請求解析器被初始化,解析和驗證一個請求是很容易的。 例如,請注意 TaskAPI.put() 方法變的多么地簡單:
def put(self, id):
task = filter(lambda t: t['id'] == id, tasks)
if len(task) == 0:
abort(404)
task = task[0]
args = self.reqparse.parse_args()
for k, v in args.iteritems():
if v != None:
task[k] = v
return jsonify( { 'task': make_public_task(task) } )
使用 Flask-RESTful 來處理驗證的另一個好處就是沒有必要單獨地處理類似 HTTP 400 錯誤,Flask-RESTful 會來處理這些。
## 生成響應
原來設計的 REST 服務器使用 Flask 的 jsonify 函數來生成響應。Flask-RESTful 會自動地處理轉換成 JSON 數據格式,因此下面的代碼需要替換:
return jsonify( { 'task': make_public_task(task) } )
現在需要寫成這樣:
return { 'task': make_public_task(task) }
Flask-RESTful 也支持自定義狀態碼,如果有必要的話:
return { 'task': make_public_task(task) }, 201
Flask-RESTful 還有更多的功能。make_public_task 能夠把來自原始服務器上的任務從內部形式包裝成客戶端想要的外部形式。最典型的就是把任務的 id 轉成 uri。Flask-RESTful 就提供一個輔助函數能夠很優雅地做到這樣的轉換,不僅僅能夠把 id 轉成 uri 并且能夠轉換其他的參數:
from flask.ext.restful import fields, marshal
task_fields = {
'title': fields.String,
'description': fields.String,
'done': fields.Boolean,
'uri': fields.Url('task')
}
class TaskAPI(Resource):
# ...
def put(self, id):
# ...
return { 'task': marshal(task, task_fields) }
task_fields 結構用于作為 marshal 函數的模板。fields.Uri 是一個用于生成一個 URL 的特定的參數。 它需要的參數是 endpoint。
## 認證
在 REST 服務器中的路由都是由 HTTP 基本身份驗證保護著。在最初的那個服務器是通過使用 Flask-HTTPAuth 擴展來實現的。
因為 Resouce 類是繼承自 Flask 的 MethodView,它能夠通過定義 decorators 變量并且把裝飾器賦予給它:
from flask.ext.httpauth import HTTPBasicAuth
# ...
auth = HTTPBasicAuth()
# ...
class TaskAPI(Resource):
decorators = [auth.login_required]
# ...
class TaskAPI(Resource):
decorators = [auth.login_required]
# ...
- 空白目錄
- serial
- serial 整理
- hex to str
- Python3 字符編碼
- 字符串編碼與Python 3編碼
- python3中bytes和string之間的互相轉換
- Python3 字符編碼
- python整數、字符串、字節串相互轉換
- python整數、字符串、字節串相互轉換
- python常用的十進制、16進制、字符串、字節串之間的轉換(長期更新帖)
- python中pyserial模塊使用方法
- 談談 Python 程序的運行原理
- flask
- Flask 在 Debug 模式下初始化2次
- Flask中向前端傳遞或者接收Json文件的方法
- 使用 Python 和 Flask 設計 RESTful API
- 使用 Flask-RESTful 設計 RESTful API