第47天:Web 開發 RESTful


圖片

如今單頁 Web 項目很流行,使用各類 Js 框架,經過 Ajax 和服務器的 Api 進行交互,實現相似原生 app 效果,很酷,對 Flask 來講小菜一碟,是時候瞭解下 Flask-RESTful 了html

開始前先了解下 RESTful,阮一峯老師有這樣的解釋:前端

網絡應用程序,分爲前端和後端兩個部分。當前的發展趨勢,就是前端設備層出不窮(手機、平板、桌面電腦、其餘專用設備......)。所以,必須有一種統一的機制,方便不一樣的前端設備與後端進行通訊。這致使API構架的流行,甚至出現"API  First"的設計思想。RESTful API是目前比較成熟的一套互聯網應用程序的API設計理論python

也就是說 RESTful 一個框架和互聯網應用的設計原則,遵循這個設計原則,可讓應用脫離前臺展示的束縛,支持不一樣的前端設備。程序員

安裝

Flask 的 RESTful 模塊是 flask-restful ,使用 pip 安裝:sql

pip install flask-restful

若是安裝順利,能夠在 Python Shell 環境下導入json

>>> from flask_restful import Api>>>

小試牛刀

安裝好後,簡單試試。flask-restful  像以前的 bootstrop-flask 以及 flask-sqlalchamy 模塊同樣,使用前須要對 Flask  應用進行初始化,而後會獲得當前應用的 api 對象,用 api 對象進行資源綁定和路由設置:flask

from flask import Flaskfrom flask_restful import Api, Resource
app = Flask(__name__)
api = Api(app)  # 初始化獲得 api 對象

上面代碼中從 flask_restful 中引入的 Resource 類是用來定義資源的,具體資源必須是 Resource 的子類,下面定義一個 HelloRESTful 資源:後端

class HelloRESTful(Resource):    def get(self):        return {'greet': 'Hello Flask RESTful!'}

接着,給資源綁定 URI:api

api.add_resource(HelloRESTful, '/')
if __name__ == '__main__':   # 別忘了啓動應用的代碼    app.run(debug=True)

在終端或者命令行下運行 python app.py 啓動應用服務器

訪問 localhost:5000 或者 127.0.0.1:5000 查看效果,將會看到 JSON 格式的數據輸出:

{  "greet": "Hello Flask RESTful!"}

也能夠用 curl 工具在終端或者命令行下發送請求:

 curl http://localhost:5000 -s{    "greet": "Hello Flask RESTful!"}

curl 的參數 -s 是開啓安靜模式的意思

資源

從上面代碼中能夠看到,資源是 Resource 類的子類,以請求方法( GET、POST 等)名稱的小寫形式定義的方法,能對對應方法的請求做出相應,例如上面資源類中定義的 get 方法能夠對 GET 請求做出相應,還能夠定義 putpostdelete 等,稱之爲視圖方法。

例如建立一個 todo 字樣,支持獲取代辦事項和新增代辦事項:

# 初始化待辦列表todos = {  'todo_1': "讀《程序員的自我修養》",  'todo_2': "買點吃的",  'todo_3': "去看星星"}class Todo(Resource):    # 根據 todo_id 獲取代辦事項    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]}
  • 經過 GET 方式,提供 todo_id, 從 todos 列表中獲取待辦事項內容
  • 經過 PUT 方式,提供 todo_id, 從請求體中獲取到內容,做爲待辦事項內容
  • 兩種方法都返回 todo_id 所對應的待辦事項內容

爲 Todo 資源指定 URI:

api.add_resource(Todo, '/todo/<string:todo_id>/')

啓動項目,用 curl 工具測試:

# 讀取 key 爲 todo_1 的待辦事項 curl http://localhost:5000/todo/todo_1/{    "todo_1": "\u8bfb\u300a\u7a0b\u5e8f\u5458\u7684\u81ea\u6211\u4fee\u517b\u300b"}
# 建立一個 key 爲 todo_4 的代辦事項 curl http://localhost:5000/todo/todo_4/ -d "data=學習 Flask" -X PUT{    "todo_4": "\u5b66\u4e60 Flask"}

# 讀取剛添加的待辦事項 todo_4 curl http://localhost:5000/todo/todo_4/{    "todo_4": "\u5b66\u4e60 Flask"}

Flask-RESTful 支持多種視圖方法的返回值:

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):        # 返回內容,狀態碼以及 Header        return {'task': 'Hello world'}, 200, {'Etag': 'some-opaque-string'}

爲三個資源指定 URI:

api.add_resource(Todo1, '/todo_1/')api.add_resource(Todo1, '/todo_2/')api.add_resource(Todo1, '/todo_3/')

啓動項目後,用 curl 工具來測試:

 curl http://localhost:5000/todo_1/{    "task": "Hello world"}
# -請求 todo_2 並顯示出 HTTP 標頭,HTTP 狀態碼爲 201 curl http://localhost:5000/todo_2/ -iHTTP/1.0 201 CREATEDContent-Type: application/jsonContent-Length: 30Server: Werkzeug/0.16.0 Python/3.7.5rc1Date: Thu, 31 Oct 2019 14:12:54 GMT
{    "task": "Hello world"}
# -請求 todo_3 並顯示出 HTTP 標頭,HTTP 狀態碼爲 200 ,標頭中還有 Etag curl http://localhost:5000/todo_3/ -iHTTP/1.0 200 OKContent-Type: application/jsonContent-Length: 30Etag: some-opaque-stringServer: Werkzeug/0.16.0 Python/3.7.5rc1Date: Thu, 31 Oct 2019 14:14:57 GMT
{    "task": "Hello world"}

路由

從上面能夠看到,經過 api.add_resource 方法來爲資源設置路由

第一個參數是資源類,第二個參數是路由,和以前介紹的 @app.route 註解參數同樣

能夠爲一個資源制定多個理由,例如:

api.add_resource(Todo, '/todo/', '/mytodo/')

http://localhost:5000/todo/http://localhost:5000/mytodo/ 都將指向 Todo

既然路由,就應該有 endpoint,經過命名參數 endpoint 指定:

api.add_resource(Todo, '/todo/', endpoint='todo_ep')

設置路由的 endpointtodo_ep,若是不指定,endpoint 就是資源類名的小寫形式

endpoint 是 Flask 中對具體路由的內部的具體定義,通常做爲 url_for 方法的第一個參數,即經過 endpoint 得到該路由的 URL,在列出 RESTful 資源 URL 時很是有用。

請求解析

RESTful 服務器對請求數據有很強的依賴,就請求數據的獲取及校驗是很繁瑣的事情,還好 Flask-RESTful 提供了很是好的請求解析工具 reqparse,不只能夠獲取請求數據,還能夠對數據進行校驗並返回合適的錯誤消息。

from flask_restful import reqparse  # 引入 reqparse 模塊# ...
parser = reqparse.RequestParser()  # 定義全局的解析實體# 定義參數 id,類型必須是整數parser.add_argument('id', type=int, help='必須提供參數 id')# 定義參數 name,且爲必填parser.add_argument('name', required=True)# ...
class Reqparser(Resource):    def get(self):        args = parser.parse_args()  # 獲取解析器中定義的參數 並校驗        return args
api.add_resource(Reqparser, '/reqparser/')  # 指定路由

看下效果:

# 提供一個非整數參數 id curl http://localhost:5000/reqparser/ -d "id=noint" -X GET{    "message": {        "id": "\u53c2\u6570 id \u5fc5\u987b\u662f\u6574\u6570"    }}
# 不提供參數 namecurl http://localhost:5000/reqparser/{    "message": {        "name": "Missing required parameter in the JSON body or the post body or the query string"    }}
  • 當參數校驗失敗,自動返回 400 狀態碼,以及錯誤信息,經過命名參數 help 設置錯誤信息,不提供會有默認信息,如比選參數 name 的錯誤信息。
  • 默認狀況下有多個參數錯誤,會以定義參數的順序,逐個顯示錯誤,定義解析器時將 bundle_errors 設置爲 True,則可顯示多個錯誤,如 parser = reqparse.RequestParser(bundle_errors=True),或者設置應用配置,如 app.config['BUNDLE_ERRORS'] = True
  • 默認狀況下參數都是從請求表單中獲取,定義參數時命名參數 location 能夠指定從 form、headers、args(即 querystring)仍是從 cookies 等中獲取,如 parser.add_argument('id', type=int, help='必須提供參數 id', location='args')

請求解析器支持繼承,能夠定義最高級別的解析器,逐漸細化,最後應用的具體資源上:

from flask_restful import reqparse
parser = reqparse.RequestParser()parser.add_argument('foo', type=int)
parser_copy = parser.copy()  # 繼承parser_copy.add_argument('bar', type=int)  # parser_copy 將有兩個參數
# 改變繼承來的參數 foo 必填且的獲取位置爲 querystringparser_copy.replace_argument('foo', required=True, location='args')
# 刪除繼承來的參數 fooparser_copy.remove_argument('foo')

格式化輸出

請求解析處理用收到的信息,對於輸入的信息也能夠處理,經過 Flask-RESTful 提供的類 fields 和註解 marshal_with 來實現:

from flask_restful import Resource, fields, marshal_with
resource_fields = {    'name': fields.String,    'address': fields.String,    'date_updated': fields.DateTime(dt_format='rfc822'),}
class TodoFormat(Resource):    @marshal_with(resource_fields, envelope='resource')    def get(self):        return db_get_todo()  # 某個得到待辦事項的方法
  • 定義一個字段格式化模板,屬性用 fields 的類型方法定義
  • 在響應方法上加上 marshal_with 註解,指定格式化模板,和封裝屬性名

格式化模板屬性名,須要在響應函數返回的對象屬性中匹配,若是須要會要對字段重命名,能夠這樣:

fields = {    # name 將被重命名爲 private_name    'name': fields.String(attribute='private_name'),    'address': fields.String}

返回值中沒有能夠定義默認值:

fields = {    # 爲 name 設置默認值    'name': fields.String(default='Anonymous User'),    'address': fields.String}

總結

本節課程簡單介紹了 Flask 如何玩 RESTful,經過對 RESTful 的說明,講解了 Flask-RESTful 模塊的用法,並簡單講解了資源、路由,以及請求解析和格式化輸出等技術

示例代碼:Python-100-days-day047

參考

  • http://www.ruanyifeng.com/blog/2014/05/restful_api.html
  • http://www.ruanyifeng.com/blog/2011/09/restful.html
  • https://flask-restful.readthedocs.io/en/latest/
  • http://www.ruanyifeng.com/blog/2019/09/curl-reference.html

系列文章

    第46天:Flask數據持久化

    第45天:Web表單

    第44天:Flask 框架集成Bootstrap

    第43天:Python filecmp&difflib模塊

    第42天:Python paramiko 模塊

    第41天:Python operator 模塊

    第0-40天:從0學習Python 0-40合集
相關文章
相關標籤/搜索