使用 Flask-RESTful 設計 RESTful API

前面我已經用 Flask 實現了一個 RESTful 服務器。今天咱們將會使用 Flask-RESTful 來實現同一個 RESTful 服務器,Flask-RESTful 是一個能夠簡化 APIs 的構建的 Flask 擴展。python

RESTful 服務器

做爲一個提醒, 這裏就是待完成事項列表 web service 所提供的方法的定義:web

==========  ===============================================  =============================
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]  刪除任務
==========  ================================================ =============================

這個服務惟一的資源叫作「任務」,它有以下一些屬性:json

  • id: 任務的惟一標識符。數字類型。
  • title: 簡短的任務描述。字符串類型。
  • description: 具體的任務描述。文本類型。
  • done: 任務完成的狀態。布爾值。

路由

在上一遍文章中,我使用了 Flask 的視圖函數來定義全部的路由。flask

Flask-RESTful 提供了一個 Resource 基礎類,它可以定義一個給定 URL 的一個或者多個 HTTP 方法。例如,定義一個可使用 HTTP 的 GET, PUT 以及 DELETE 方法的 User 資源,你的代碼能夠以下:api

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/<int:id>', endpoint = 'user') 

add_resource 函數使用指定的 endpoint 註冊路由到框架上。若是沒有指定 endpoint,Flask-RESTful 會根據類名生成一個,可是有時候有些函數好比 url_for 須要 endpoint,所以我會明確給 endpoint 賦值。服務器

個人待辦事項 API 定義兩個 URLs:/todo/api/v1.0/tasks(獲取全部任務列表),以及 /todo/api/v1.0/tasks/<int:id>(獲取單個任務)。咱們如今須要兩個資源:restful

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/<int:id>', endpoint = 'task') 

解析以及驗證請求

當我在之前的文章中實現此服務器的時候,我本身對請求的數據進行驗證。例如,在以前版本中如何處理 PUT 的:app

@app.route('/todo/api/v1.0/tasks/<int:task_id>', 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。ide

首先,對於每個資源須要定義參數以及怎樣驗證它們:

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] # ...
相關文章
相關標籤/搜索