Flask 請求和響應

一、請求排程

瀏覽器通過URL訪問Flask伺服器時,要通過URL和檢視函式的對映關係表找到處理該URL的檢視函式(該檢視函式返回響應給瀏覽器),這個對映關係可以通過app.route修飾器建立。下面給出了上一篇文章中的helloworld例子

#coding:utf-8
from flask import Flask
app=Flask(__name__)
@app.route("/")
def index():
return "<h1>Hello World!</h1>"
@app.route("/user/<name>")
def user(name):
return "<h1>Hello,%s</h1>"%name
if __name__=="__main__":
app.run(debug=True,host="0.0.0.0",port=8000)

例子中將程式根目錄URL和檢視函式index對映起來。可以在python命令列通過app.url_map檢視這種對映關係:

HEAD,OPTIONS,GET是URL與檢視函式的請求方法,HEAD和OPTIONS為Flask自動處理,功能分別為獲取響應報文首部(即不傳送報文主體)和詢問訪問某URL支援的方法。 可以通過route修飾器的methods引數為路由指定不同的請求方法,比較常用的是GET和POST方法。

二、程式和請求上下文

檢視函式在處理請求時,有時需要獲取一些其他物件資訊,這個時候的處理方式是使用執行緒獨立的全域性變數而非傳參,比如請求物件request等(其他上下文全域性變數如下表)。上下文可以理解為對這些物件的引用池,方便我們隨時訪問這些變數。

Flask上下文全域性變數
變數名上下文說明
current_app程式上下文當前啟用的程式例項,比如helloworld例子中的app
g程式上下文用作處理請求時的臨時儲存,每次請求都重設
request請求上下文請求物件,包含了客戶端HTTP請求的內容,例如獲取客戶端請求報文頭部中包含的
User-Agent資訊:request.headers.get(“User-Agent”)
session請求上下文使用者會話,字典格式,儲存請求間需要記住的資訊

從flask中引入上下文變數就可以使用這些變數,例如下面的檢視函式返回請求中的User-Agent給客戶端:

@app.route("/user-agent/")
def get_user_agent():
user_agent=request.headers.get("User-Agent")
return "<p>%s</p>"%user_agent

輸入URL http://127.0.0.1:8000/user-agent/ 訪問:

Falsk上下文在請求之前會先啟用,請求處理完畢則會將其刪除。

三、響應

響應即檢視函式的返回值,上述例子中的返回都很簡單,直接將html程式碼作為返回值。但是Flask Http協議的返回值中通常還會有以下幾種不同的格式:

1.元組:(html字串,狀態碼,返回報文首部資訊)

狀態響應碼:Flask預設為200(故此時可省略),即請求成功處理。一些常用的狀態碼如下:

    2xx成功:200(請求成功處理)、204(請求成功處理但無資源返回)、206(請求部分內容成功,在請求報文實體首部中包含需要的資源)

    3xx重定向:301(永久性重定向,表示訪問資源已經更新了URI,通常在返回報文首部資訊增加Location提示新的URI,如果訪問的URI儲存為書籤,則會被更新為新的URI)

                          302(臨時性重定向,表示此次訪問資源被重定向到新的URI,並非永久,不更新書籤)

                          304(資源不滿足客戶端請求條件,比如請求的是某時間點後有更新則返回新資源,但資源在該時間點後無更新,被規在3xx,但和重定向沒啥關係)

    4xx客戶端錯誤:400(客戶端請求存在錯誤,應該修改請求後再次傳送)、401(未授權)、403(禁止訪問)、404(找不到訪問的資源)

    5xx伺服器錯誤:500(伺服器處理請求時出現錯誤)、503(伺服器忙碌或者停機維護無法處理請求)

返回報文首部資訊:比如包含重定向時,包含URI的Location資訊,{“Location”:”http:xxx”},很少用到,可省略。

2.Response物件

make_response()函式生成Response物件,函式接受1、2、3個引數,引數意義和元組形式一致,可以對響應進行一些設定後返回,比如設定cookie。

from flask import make_response
@app.route("/response/")
def response():
response=make_response("<h1>This response carries a cookie</h1>")
response.set_cookie("answer","42")
return response

瀏覽器訪問後再次請求f12檢視請求頭中包含設定的cookie。

3.使用redirect函式

常用來跳轉到重定向的url,比如return redirect(”http://www.baidu.com/”)

from flask import redirect
@app.route("/redirect")
def test_redirect():
return redirect("http://www.baidu.com/")

4.使用abort函式處理錯誤

通常用來丟擲異常,把控制權交給web伺服器返回異常,比如:abort(404)

5.使用渲染模板

前述的例子都太簡單,通常一個http請求的處理可能會涉及複雜的業務邏輯和表現邏輯,比如在某網站註冊賬號的時候,通過POST方法提交一個表單給伺服器,伺服器需要通過表單內容來生成一個新使用者並進行資料庫相關操作,然後再給客戶端返回一個處理完畢的響應。模板的作用是處理表現邏輯,把業務邏輯和表現邏輯分開,提升程式碼的可維護性。Flask使用了Jinja2模板引擎。

最簡單的helloworld模板可以這樣使用:在helloworld.py同一目錄下建立templates資料夾,然後把模板檔案放在資料夾中,比如’helloworld.html”,然後通過Flask的render_template函式渲染模板,並作為檢視函式返回值即可。

from flask import render_template
@app.route("/")
def index():
return render_template("helloworld.html")

模板檔案包含響應文字:

訪問“http://127.0.0.1:8000”可以看到效果和原來一樣~

複雜的響應文字中會包含邏輯語句以及變數,變數真實值可以通過render_template傳遞。使用真實值替換模板中的變數,得到最終的響應文字,這個過程即稱為渲染模板。

三、模板

1.變數

模板中的變數使用雙大括號佔位 變數名錶示:比如{{ name }}。我們可以把helloword例子的user檢視函式替換成模板渲染後返回:

新增模板檔案templates/user.html:

<h1>Hello,{{ name }}</h1>

user檢視函式:

@app.route("/user/<name>")
def user(name):
return render_template("user.html",name=name)

訪問url可以看到效果:

上述例子使用url中的<name>字串作為變數傳遞給檢視函式,檢視函式又將之傳遞給模板渲染後返回響應。Jinja2可以識別複雜的物件,比如可以把一個使用者物件傳遞給模板,然後在模板中訪問物件的各個屬性變數。例如 {{ user.username }} 獲取user物件的username屬性。

變數可以用過濾器修改,Jinja2提供了一些常用的過濾器,使用格式為{{ 變數名|過濾器名 }}比如要將上述模板中的name變數全部以大寫字母顯示,可以修改user.html如下:

效果:

常用的Jinja2過濾器有:

過濾器名說明
safe渲染值時不轉義,預設情況下安全起見,jinja2會將html標籤在渲染時會被轉義掉(比如左尖括號轉義為&lt;),使用這個過濾器可以保護這些標籤不被過濾(對可信的變數比如伺服器自己生成的html串採用)
capitalize首字母大寫,其他字母小寫
lower小寫形式
upper大寫形式
title每個單詞首字母大寫
trim去掉值首尾空格
striptags把值中所有的HTML標籤刪除

2.控制語句,結構: {% 語句 %}

①條件語句

{% if name %}
Hello,{{ user }}!
{% else %}
Hello,Stranger!
{% endif %}

②for迴圈

<ul>
{% for user in users %}
<li>user.username</li>
{% endfor %}
</ul>

③巨集 定義格式類似於函式,比如可以定義一個巨集,然後再呼叫這個巨集,使用巨集定義的格式替換呼叫的部分

定義:關鍵字macro

{% macro render_comment(comment) %}
<li>{{ comment }}</li>
{% endmacro %}

呼叫:{{ 巨集名(引數) }}

<ul>
{% for comment in comments %}
{{ render_comment(comment) }}
{% endfor %}
</ul>

也可以將巨集單獨儲存在一個檔案中,再呼叫的檔案中使用 import語句引入。

{% import "_macro.html" as macro %}
<ul>
{% for comment in comments %}
{{ macro.render_comment(comment) }}
{% endfor %}
</ul>

④匯入程式碼片,其作用是程式碼重用,如下可將_comments.html中的程式碼片段包含到模板檔案中。

{% include "_comments.html" %}

⑤模板繼承,比如同一個網站的不同網頁通常有一些相同或相似的格式,這時候可以把這些相似的樣式寫在一個基模板中,同時預留可供擴充套件的block區域,其他頁面可通過繼承該模板,並在對應block區域擴充套件自己頁面的內容:

基模板base.html:

<!DOCTYPE html>
<html>
<head>
{% block head %}
<title>{% block title %}{% endblock %}-My App</title>
{% endblock %}
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>

繼承自base.html的index.html:

{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
    {{ supper() }}
    <style>
    </style>
{% endblock %}
{% block body %}
<h1>Hello World</h1>
{% endblock %}

子頁面擴充套件的部分會替換基模板相同的block區域包含的部分,如果有內容需要繼承基模板同時擴充套件新內容,使用supper()來獲取基模板內容。