摘要:在使用自定義響應類的應用中,和無法知道自定義類的細節,所以它們使用標準響應類來創建響應。有了這個自定義響應類,任何滿足格式要求的文檔都會自動被標記為內容類型,而其他響應則會繼續獲得默認的內容類型。
譯文鏈接:編程派
原文鏈接:Flask Web Development作者的博客
有翻譯或理解不對的地方,望大家指正!
Flask框架中的響應類,命名很貼切,叫Response。不過Flask應用中很少直接調用這個類。而是將其作為路由函數所返回響應數據的內部容器,容器里還包含了用于創建HTTP響應的其他信息。
但是沒多少人知道,Flask框架其實允許應用將默認的響應類,替換為自定義類。這就給了我們研究小竅門的機會。在本文中,我將展示如何利用Flask的這個特性,簡化你的代碼。
Flask中的響應類是如何工作的?大部分應用并不直接使用Flask中的響應類(Response class),但這并不是說這個類沒有用武之地;實際上,Flask會為每個請求創建響應對象。那么,它是如何實現的呢?
Flask用來處理請求的函數返回時,響應周期就開始了。在網絡應用中,路由通常最后會調用render_template函數,渲染引用的模板文件,將其作為字符串返回:
@app.route("/index") def index(): # ... return render_template("index.html")
但是,你可能也知道,Flask的路由函數可以選擇額外返回兩個值,這兩個值將被分別設為HTTP狀態碼和自定義的HTTP響應標頭:
@app.route("/data") def index(): # ... return render_template("data.json"), 201, {"Content-Type": "application/json"}
在上面的例子中,狀態碼被設為201,取代了Flask默認的200,即請求被成功處理的狀態碼。這個例子還定義了內容類型標頭(Content-Type header),表明HTTP響應中包含JSON數據,因為如果你不明確設置內容類型的話,Flask會默認設置為HTML。
上面的例子介紹了HTTP響應的三個基本組成部分,即數據或正文、狀態碼和標頭。Flask的應用實例擁有一個make_response函數,可以接受路由函數的返回值(可以是單個值,也可以是有1-3個值的元組),并將其填入響應對象(Response object)中。
你可以通過Python控制臺會話(console session),看看整個過程。首先創建一個虛擬環境,并安裝Flask,然后開啟Python會話,并輸入下面的代碼:
>>> from flask import Flask >>> app = Flask(__name__) >>> app.make_response("Hello, World")>>> app.make_response(("Hello, World", 201))
這里,我創建了一個簡單的Flask應用實例,之后調用了make_response()方法創建響應類對象。第一次調用時,我傳了一個字符串作為參數,所以響應對象中使用了默認的狀態碼和標頭。第二次調用時,我傳入了有兩個值的元組,強制返回了非默認的狀態碼。注意,第二次調用時使用了兩個括號,里層的括號將字符串和狀態碼包在了元組中。由于make_response()函數只接受一個參數,所以必須要這樣做。
Flask在創建了代表路由函數返回值的響應對象(Response object)之后,還會做一些處理。包括將響應對象傳入自定義的after_request處理程序(handlers),在這一步,應用還有有機會插入或修改標頭、更改正文或狀態碼,如果愿意的話,甚至是啟用嶄新的的響應對象取而代之。最后,Flask會獲取最終的響應對象,渲染成HTTP響應,并發送給客戶端。
Flask中的響應類我們來看看響應類中最有趣的特性。下面的類定義,展示了我眼中這個類所具備的靈活屬性和方法:
class Response: charset = "utf-8" default_status = 200 default_mimetype = "text/html" def __init__(self, response=None, status=None, headers=None, mimetype=None, content_type=None, direct_passthrough=False): pass @classmethod def force_type(cls, response, environ=None): pass
注意,如果你去翻閱Flask的源碼,是找不到上述類定義的。Flask中的Response類,實際上衍生自Werkzeug庫中的一個同名類。而Werzeug中的Response類繼承的是BaseResponse類,這個類中就包含了上述定義。
charset、default_status和default_mimetype這三個類屬性定義了相應的默認值。如果任何一個默認值不適用你的應用,那么你可以創建Response類的子類,定義你自己的默認值,而不必在每一個響應對象中設置自定義值。例如,如果你的應用是一個所有的路由均返回XML格式數據的API接口,你就可以在自定義的類中,將default_mimetype改為application/xml,這樣Flask就會默認返回XML響應。稍后你會看到如何實現。
這里,我不會詳細介紹__init__構造函數(你可以閱讀Werkzeug的文檔),但請注意,Flask響應對象中的三個重要元素,即響應正文、狀態碼和標頭,是作為參數傳入的。在子類中,構造函數可以改變創建響應的相應規則。
響應類中的force_type()類方法,是唯一比較復雜,但又很重要的元素。有時候,Werkzeug或是Flask需要自行創建響應對象,比如出現應用錯誤,并需要將其返回給客戶端時。在這種情況下,響應對象不是應用提供的,而是由框架創建的。在使用自定義響應類的應用中,Flask和Werkzeug無法知道自定義類的細節,所以它們使用標準響應類來創建響應。響應類中的force_type()方法,被設計為可以接受不同響應類的實例,并會將其轉換成自身的格式。
我敢肯定,你一定被force_type()方法的描述搞糊涂了。說白了,就是如果Flask碰到了一個不是其期望的響應對象,就會使用該方法進行轉換。我下面要講的第三個使用場景,就利用了這個特點,讓Flask的路由函數返回諸如字典、列表或者是其他任何自定義對象,作為請求的響應對象。
好了,理論就講這么多了。接下來,我來告訴大家如何應用上面有關響應類的小技巧。準備好了嗎?
使用自定義的響應類到現在為止,我確定你也會認為:在部分有趣的場景下,使用自定義的響應類是有利的。在給出實際例子之前,我想告訴你在Flask中設置并使用自定義的響應類是多么的簡單。請看下面的這個例子:
from flask import Flask, Response class MyResponse(Response): pass app = Flask(__name__) app.response_class = MyResponse # ...
在上面的代碼中,我定義了一個名叫MyResponse的自定義響應類。通常,自定義響應類會增加或修改默認類的行為,所以一般都會通過創建Flask中Response類的子類來實現。要想讓Flask使用自定義類,我只需要設置app.response_class即可。
Flask類中的response_class是一個類屬性,所以我們可以稍微修改上面的例子,創建一個設置了自定義響應類的Flask子類:
from flask import Flask, Response class MyResponse(Response): pass class MyFlask(Flask) response_class = MyResponse app = MyFlask(__name__) # ...例1:更改響應對象的默認值
第一個例子極其簡單。假設你的應用中大部分或全部端點(endpoints)都返回的是XML。對于這樣的應用,將默認的內容類型設置為application/xml是合理的??梢酝ㄟ^下面這個僅有兩行代碼的響應類輕松實現:
class MyResponse(Response): default_mimetype = "application/xml"
容易,對吧?如果將其設為應用的默認響應類,那么你在編寫返回XML的函數時,就不用擔心忘記設置內容類型了。舉個例子:
@app.route("/data") def get_data(): return """""" John Smith
上面這個路由使用的是默認響應類,其內容類型會被設置為text/html,因為那是默認類型。使用自定義響應類,可以免去你在所有XML路由的返回語句中,額外加上標頭的麻煩。另外,如果有的路由需要其他的內容類型,你仍可以替換掉默認值,就像對待一般的響應類一樣。
@app.route("/") def index(): return "例2:自動決定內容類型Hello, World!
", {"Content-Type": "text/html"}
下一個例子更復雜一點。假設我們的應用中HTML路由與XML路由的數量差不多,所以按照第一個例子的做法就不行了,因為不管你選用哪種默認類型,都會有一半的路由需要替換內容類型。
更好的解決辦法,則是創建一個能夠通過分析響應文本,決定正確的內容類型的響應類。下面的這個類實現了該功能:
class MyResponse(Response): def __init__(self, response, **kwargs): if "mimetype" not in kwargs and "contenttype" not in kwargs: if response.startswith("在這個簡單的例子中,我首先確保響應對象中沒有明確設置內容類型。然后,我檢查響應的正文是否以開頭,是的話就意味著數據是XML文檔格式。如果兩個條件同時成立,我會在傳入父類構造函數的參數中,插入XML內容類型。
有了這個自定義響應類,任何滿足XML格式要求的文檔都會自動被標記為XML內容類型,而其他響應則會繼續獲得默認的內容類型。而且,在所有的類中,我仍然可以在必要時聲明內容類型。
例3:自動返回JSON響應最后一個例子,針對的是利用Flask創建API接口時常見的一個小問題。API接口通常返回的是JSON凈負荷(JSON Payload,這就要求你使用jsonify()函數將Python字典類型轉換成JSON數據,并且還得在響應對象中將內容類型設置為JSON內容類型。請看下面這個例子:
@app.route("/data") def get_data(): return jsonify({"foo": "bar"})問題是,每個返回JSON的路由都需要這樣處理,那么對接口數量眾多的的API來說,你就得大量重復調用jsonify()函數。從代碼可讀性角度來講,你按照下面的方式處理是不是更好?
@app.route("/data") def get_data(): return {"foo": "bar"}下面是一個支持使用上述語法的自定義響應類,它不會影響應用中使用其他內容類型的路由正常工作:
class MyResponse(Response): @classmethod def force_type(cls, rv, environ=None): if isinstance(rv, dict): rv = jsonify(rv) return super(MyResponse, cls).force_type(rv, environ)這個例子需要稍微解釋一下,因為比較復雜。Flask僅認可一小部分的類型,作為路由函數能夠返回的有效響應類型?;旧?,你可以返回任意與字符串和二進制相關的類型(str、unicode、bytes、bytearray)。如果你喜歡,甚至可以返回一個已經創建好的響應對象。如果你返回的是字符串或二進制類型,Flask會發現這些是響應類知道如何處理的類型,并會將你返回的數據直接傳入響應類的構造函數。
但是,如果你返回的是不支持的類型,比如說上述例子中的字典,會發生什么情況?如果返回的響應類型不是Flask預期的,那么Flask就會默認它是未知響應對象,不會以其為參數創建響應對象了,而是使用響應類的force_type()類方法,強制轉換未知類型。上面的例子中,響應子類替換了該方法,但僅僅是通過調用jsonify()進行轉換,之后就會讓基類接手處理,就好像什么都沒發生一樣。
是個很好的竅門吧?尤其是這樣做不會影響其他響應的正常工作。對于返回正常響應類型的路由,該子類不會做任何處理,所有的調用請求會全部傳入父類中。
結語我希望本文能夠幫助大家更好地理解FLask中響應對象的工作原理。如果你知道其他使用Flask響應類的小竅門,請務必與我分享!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/44191.html
摘要:中使用類字典對象來獲取請求頭信息,使用獲取請求數據,如果發送類型是,則可以使用來獲取數據。響應使用類處理響應。認證使用下面的代碼可以處理。 原創譯文,如需轉載,請聯系譯者。 我的簡書博客:nummy 原文地址:Implementing a RESTful Web API with Python & Flask 簡介 首先,安裝Flask pip install flask 閱讀這篇文章...
摘要:最經典的就是了暫時性重定向的狀態碼是,表示頁面暫時性被跳轉,比如訪問一個需要權限的網址,如果當前用戶沒有登錄,應該重定向到登錄頁面。作為額外的消息頭如果以上的條件都不滿足,會假設返回值是一個合法的應用程序,并通過轉換成一個請求對象。 flask基礎之一 hello world #從flask這個包中導入Flask這個類 #Flask這個類是項目的核心,以后的很多操作都是基于這個類的對象...
閱讀 1588·2021-10-18 13:35
閱讀 2370·2021-10-09 09:44
閱讀 826·2021-10-08 10:05
閱讀 2724·2021-09-26 09:47
閱讀 3583·2021-09-22 15:22
閱讀 441·2019-08-29 12:24
閱讀 2006·2019-08-29 11:06
閱讀 2864·2019-08-26 12:23