Flask之Request請求(3)

對於web應用,客戶端與服務端之間的數據交互至關重要。在 Flask 中由全局的 request 對象來提供這些信息。

1 Request對象屬性

request中包含了前端發送過來的所有請求數據

  • form和data是用來提取請求體數據
  • 當前端通過存表單提交的時候content-type:urlencode的時候,所有數據已經被flask封裝到了form裏面。
  • 如果時候form-data多類型數據,可以通過request.data獲取
  • 通過requset.form可以直接提取請求體中的表單格式的數據, 是一個類字典的對象
  • 通過get方法只能拿到多個同名參數的第一個

Request對象的重要屬性如下:

屬性 說明
form post請求參數, 存儲結構個args一致, 它是一個字典對象,包含表單參數及其值的鍵和值對。默認是接收post參數, 還可以接收PUT,PATCH參數
args get請求參數,args是一個ImmutableMultiDict對象,類字典結構對象。解析查詢字符串的內容,它是問號(?)之後的URL的一部分。
cookie 保存Cookie名稱和值的字典對象。
files 與上傳文件有關的數據。
method 當前請求方式。
path 路由中的路徑
url 完整請求地址
base_url 去掉GET參數的URL
get_json 如果前端傳的json,可以調此接口,自動把前端傳的json轉化爲字典。使用要求前端傳遞的content-type:application/json

2 表單數據

當前請求的 HTTP 方法可通過 method 屬性來訪問。通過:attr:~flask.request.form 屬性來訪問表單數據( POST 或 PUT 請求提交的數據)。
在以下示例中,’/’ URL會呈現具有表單的網頁(student.html)。
填入的數據會發布到觸發 result()函數的’/result’ URL。
results()函數收集字典對象中的request.form中存在的表單數據,並將其發送給result.html。
該模板動態呈現表單數據的HTML表格。

from flask import Flask, render_template
from flask import request
app = Flask(__name__)

@app.route('/')
def student():
   return render_template('student.html')

@app.route('/result',methods = ['POST', 'GET'])
def result():
   if request.method == 'POST':
      result = request.form
      return render_template("result.html", result=result)

if __name__ == '__main__':
   app.run(debug=True)

student.html的HTML腳本:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
  <form action = "http://localhost:5000/result" method = "POST">
     <p>Name <input type = "text" name = "Name" /></p>
     <p>Physics <input type = "text" name = "Physics" /></p>
     <p>Chemistry <input type = "text" name = "chemistry" /></p>
     <p>Maths <input type ="text" name = "Mathematics" /></p>
     <p><input type = "submit" value = "submit" /></p>
  </form>
</body>
</html>

result.html的HTML腳本:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
<table border = 1>
    {% for key, value in result.items() %}
    <tr>
       <th> {{ key }} </th>
        <td> {{ value }} </td>
   </tr>
    {% endfor %}

</table>
</body>
</html>

運行Python腳本,並在瀏覽器中輸入URL http://localhost:5000/
在這裏插入圖片描述
當點擊提交按鈕時,表單數據以HTML表格的形式呈現在result.html上
在這裏插入圖片描述

3 Cookies

Cookie以文本文件的形式存儲在客戶端的計算機上。其目的是記住和跟蹤與客戶使用相關的數據,以獲得更好的訪問者體驗和網站統計信息。

Request對象包含Cookie的屬性。它是所有cookie變量及其對應值的字典對象,客戶端已傳輸。除此之外,cookie還存儲其網站的到期時間,路徑和域名。

在Flask中,對響應對象設置cookie。使用make_response()函數從視圖函數的返回值獲取響應對象。之後,使用響應對象的set_cookie()函數來存儲cookie。

讀回cookie很容易。request.cookies屬性的get()方法用於讀取cookie。
你可以通過 cookies 屬性來訪問 Cookies,用響應對象的 set_cookie 方法來設置 Cookies。請求對象的 cookies 屬性是一個內容爲客戶端提交的所有 Cookies 的字典。如果你想使用會話,請不要直接使用 Cookies .

讀取 cookies:

from flask import request
@app.route('/')
def index():
    username = request.cookies.get('username')
    # use cookies.get(key) instead of cookies[key] to not get a
    # KeyError if the cookie is missing.

存儲 cookies:

from flask import make_response

@app.route('/')
def index():
    resp = make_response(render_template(...))
    resp.set_cookie('username', 'the username')
    return resp

Cookies 是設置在響應對象上的。由於通常視圖函數只是返回字符串,之後 Flask 將字符串轉換爲響應對象。如果你要顯式地轉換,你可以使用 make_response() 函數然後再進行修改。

用法示例:
當訪問’/’ URL時,會打開一個簡單的表單index.html,可以設置UserID。

from flask import Flask, render_template, make_response
from flask import request
app = Flask(__name__)

@app.route('/')
def index():
   return render_template('index.html')

@app.route('/setcookie', methods=["POST", "GET"])
def setcookie():
    if request.method == "POST":
        user = request.form['nm']
        resp = make_response(render_template('readcookie.html'))
        resp.set_cookie('UserID', user)
        return resp

@app.route('/getcookie')
def getcookie():
   name = request.cookies.get('UserID')
   return f"<h1>welcome {name} </h1>"

if __name__ == '__main__':
   app.run(debug=True)

此HTML頁面包含一個文本輸入。表單發佈到’/ setcookie’ URL。相關聯的視圖函數設置Cookie名稱userID並呈現另一個頁面。

<html>
   <body>
      <form action="/setcookie" method="POST">
         <p><h3>輸入用戶名</h3></p>
         <p><input type = 'text' name = 'nm'/></p>
         <p><input type = 'submit' value = 'Login'/></p>
      </form>
   </body>
</html>

readcookie.html中,點擊超鏈接時發佈到’/getcookie’ URL,獲取cookie內容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
    <a href="/getcookie">Click here to read cookie</a>

</body>
</html>

運行python代碼,在瀏覽器中輸入http://127.0.0.1:5000/
在這裏插入圖片描述
點擊login
在這裏插入圖片描述
點擊超鏈接
在這裏插入圖片描述

4 會話session

除請求對象之外,還有一個 session 對象。它允許你在不同請求間存儲特定用戶的信息。它是在 Cookies 的基礎上實現的,並且對 Cookies 進行密鑰簽名。這意味着用戶可以查看你 Cookie 的內容,但卻不能修改它,除非用戶知道簽名的密鑰。
要使用會話,你需要設置一個密鑰。

與Cookie不同,Session(會話)數據存儲在服務器上。會話是客戶端登錄到服務器並註銷服務器的時間間隔。需要在該會話中保存的數據會存儲在服務器上的臨時目錄中。
爲每個客戶端的會話分配會話ID。會話數據存儲在cookie的頂部,服務器以加密方式對其進行簽名。對於此加密,Flask應用程序需要一個定義的SECRET_KEY。
Session對象也是一個字典對象,包含會話變量和關聯值的鍵值對。
例如,要設置一個’username’會話變量,請使用以下語句:

Session[‘username’] = ’admin’

要釋放會話變量,使用pop()方法。

session.pop('username', None)

以下代碼是Flask中的會話工作的簡單演示。URL '/‘只是提示用戶登錄,因爲未設置會話變量’username’。

from flask import Flask, redirect, session, escape, url_for
from flask import request
app = Flask(__name__)

@app.route('/')
def index():
    if "username" in session:
        return 'Logged in as %s' % escape(session['username'])
    return '''You are not logged in <br><a href = '/login'></b>click here to log in</b></a>'''

當用戶瀏覽到點擊login按鈕時,因爲它是通過GET方法調用的,所以將打開一個登錄表單。
表單發送回’/login’,現在會話變量已設置。應用程序重定向到’/’。此時會話變量’username’被找到。

@app.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == "POST":
        session['username'] = request.form['username']
        return redirect(url_for('index'))
    return '''
        <form action="" method="post">
            <p><input type=text name=username>
            <p><input type=submit value=Login>
        </form>
    '''

應用程序還包含一個logout()視圖函數,它會彈出’username’會話變量。並且跳轉到首頁

@app.route('/logout')
def logout():
    # remove the username from the session if it's there
    session.pop('username', None)
    return redirect(url_for('index'))

設置密鑰:

# set the secret key.  keep this really secret:
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'

執行代碼:

if __name__ == '__main__':
   app.run(debug=True)

瀏覽器輸入URL:http://127.0.0.1:5000/
在這裏插入圖片描述
點擊超鏈接
在這裏插入圖片描述
點擊login
在這裏插入圖片描述

5 重定向和錯誤

redirect()函數:返回一個響應對象,並將用戶重定向到具有指定狀態代碼的另一個目標位置。
函數說明如下:

Flask.redirect(location, statuscode, response)
  • location:應該重定向的目標URL
  • statuscode:發送到瀏覽器的狀態碼,默認是302,一般不動
  • response:實例響應

abort() 函數:放棄請求並返回錯誤代碼
函數說明:

Flask.abort(code)

Code參數採用以下值之一:

  • 400 - 用於錯誤請求
  • 401 - 用於未身份驗證的
  • 403 - Forbidden
  • 404 - 未不到
  • 406 - 表示不接受
  • 415 - 用於不支持的媒體類型
  • 429 - 請求過多

用法示例:

from flask import Flask, redirect, session, escape, url_for, abort
from flask import request
app = Flask(__name__)

@app.route('/')
def index():
    if "username" in session:
        return 'Logged in as %s' % escape(session['username'])
    return '''You are not logged in <br><a href = '/login'></b>click here to log in</b></a>'''

@app.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == "POST":
        session['username'] = request.form['username']
        if session['username'] == 'admin':
            return redirect(url_for('index'))
        else:
            abort(401)
    return '''
        <form action="" method="post">
            <p><input type=text name=username>
            <p><input type=submit value=Login>
        </form>
    '''
@app.route('/logout')
def logout():
    # remove the username from the session if it's there
    session.pop('username', None)
    return redirect(url_for('index'))

# set the secret key.  keep this really secret:
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'

if __name__ == '__main__':
   app.run(debug=True)

運行代碼,瀏覽器輸入URL:http://127.0.0.1:5000
在這裏插入圖片描述
輸入非admin用戶,頁面顯示:
在這裏插入圖片描述
默認情況下,錯誤代碼會顯示一個黑白的錯誤頁面。如果你要定製錯誤頁面, 可以使用 errorhandler() 裝飾器
代碼示例:

@app.errorhandler(401)
def aut_not_found(error):
    return render_template('auth_not_found.html'), 401

@app.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == "POST":
        session['username'] = request.form['username']
        if session['username'] == 'admin':
            return redirect(url_for('index'))
        else:
            abort(401)
    return '''
        <form action="" method="post">
            <p><input type=text name=username>
            <p><input type=submit value=Login>
        </form>
    '''

auth_not_found.html內容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
    page not found, 401 Forbidden!!!
</body>
</html>

當輸入用戶非admin時,運行輸出:
在這裏插入圖片描述

6 響應response

視圖函數的返回值會被自動轉換爲一個響應對象。如果返回值是一個字符串, 它被轉換爲該字符串爲主體的、狀態碼爲 200 OK的 、 MIME 類型是text/html 的響應對象。Flask 把返回值轉換爲響應對象的邏輯是這樣:

  • 如果返回的是一個合法的響應對象,它會從視圖直接返回。
  • 如果返回的是一個字符串,響應對象會用字符串數據和默認參數創建。
  • 如果返回的是一個元組,且元組中的元素可以提供額外的信息。這樣的元組必須是 (response, status, headers) 的形式,且至少包含一個元素。 status 值會覆蓋狀態代碼, headers 可以是一個列表或字典,作爲額外的消息標頭值。
  • 如果上述條件均不滿足, Flask 會假設返回值是一個合法的 WSGI 應用程序,並轉換爲一個請求對象。
    常用處理response方式:
    (1)使用元祖,返回自定義的響應信息
#              響應體       狀態碼 響應頭
# return "index page", 400, [("aimaile", "1"), ("City", "c1")]
# return "index page", 400, {"aimaile": "2", "City1": "cq"}
# return "index page", 666, {"aimaile": "3", "City1": "cq"}
# return "index page", "666 aimaile status", {"aimaile": "python1", "City1": "cq"}
# return "index page", "666 aimaile status"

(2)使用make_response 來構造響應信息
如果你想在視圖裏操縱上述步驟結果的響應對象,可以使用 make_response() 函數。
譬如你有這樣一個視圖:

@app.errorhandler(404)
def not_found(error):
    return render_template('error.html'), 404

你只需要把返回值表達式傳遞給 make_response() ,獲取結果對象並修改,然後再返回它:

@app.errorhandler(404)
def not_found(error):
    resp = make_response(render_template('error.html'), 404)
    resp.headers['X-Something'] = 'A value'
    return resp

(3) 返回json
目前最常用的是返回json。終端和平臺交互現在一般都是json。
當前很流行前後分離,前端用vue,前後交互也用json,後臺只管數據處理,前端只管渲染數據。
jsonify()幫助將字典類型轉爲json數據

@app.route("/list")
def loveCity():
    # json就是字符串
    data = {
        "city": "cq",
        "old": 11
    }
    # json.dumps(字典)  將python的字典轉換爲json字符串
    #  json.loads(字符串)  將字符串轉換爲python中的字典
    # json_str = json.dumps(data)
    # return json_str, 200, {"Content-Type": "application/json"}
    # jsonify幫助轉爲json數據,並設置響應頭 Content-Type 爲application/json
    # return jsonify(data)
    return jsonify(city="cq", country="china")

7 文件上傳/下載

文件上傳
在Flask中處理文件上傳非常簡單。它需要一個HTML表單,其enctype屬性設置爲“multipart / form-data”,將文件發佈到URL。工作流程如下:

  • 一個 標籤被標記有 enctype=multipart/form-data ,並且在裏面包含一個 標籤
  • 服務端應用通過請求對象上的 files 字典訪問文件。
  • 使用文件的 save() 方法將文件永久地保存在文件系統上的某處。

目標文件的名稱可以是硬編碼的,也可以從request.files[file]對象的filename屬性中獲取。但是,建議使用secure_filename()函數獲取它的安全版本。
可以在Flask對象的配置設置中定義默認上傳文件夾的路徑和上傳文件的最大大小。

配置 說明
app.config[‘UPLOAD_FOLDER’] 定義上傳文件夾的路徑
app.config[‘MAX_CONTENT_PATH’] 指定要上傳的文件的最大大小(以字節爲單位)

upload.html內容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
<form action="http://localhost:5000/uploader" method="POST" enctype="multipart/form-data">
<input type="file" name="file"/>
<input type="submit" value="上傳"/>
</form>

</body>
</html>

上傳文件代碼:

import os
from flask import Flask, redirect, session, escape, url_for, abort, render_template
from flask import request
from werkzeug.utils import secure_filename

UPLOAD_FOLDER = './uploads'

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024

@app.route('/uploader', methods = ['GET', 'POST'])
def upload_file():
   if request.method == 'POST':
      f = request.files['file']
      filename = secure_filename(f.filename)
      f.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
      return 'file uploaded successfully'
   else:
      return render_template('upload.html')

if __name__ == '__main__':
   app.run(debug=True)

在瀏覽器輸入URL:http://localhost:5000/uploader
顯示上傳文件頁面如下:
在這裏插入圖片描述
選擇文件後,點擊“上傳”

查看uploads目錄,能看到上傳的文件
在這裏插入圖片描述

文件下載
在Flask想要實現文件下載功能,只需要導入

send_from_directory(directory, filename, **options)

注意:
這裏主要需要注意的是send_from_directory方法,經過實測,需加參數as_attachment=True,否則對於圖片格式、txt格式,會把文件內容直接顯示在瀏覽器,對於xlsx等格式,雖然會下載,但是下載的文件名也不正確。
代碼示例:

import os
from flask import Flask, abort, render_template, send_from_directory
from flask import request
from werkzeug.utils import secure_filename

UPLOAD_FOLDER = './uploads'

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024

@app.route('/uploader', methods = ['GET', 'POST'])
def upload_file():
   if request.method == 'POST':
      f = request.files['file']
      filename = secure_filename(f.filename)
      f.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
      return 'file uploaded successfully'
   else:
      return render_template('upload.html')

@app.route('/download/<filename>')
def download(filename):
   if request.method == "GET":
      if os.path.isfile(os.path.join('uploads', filename)):
         return send_from_directory('uploads', filename, as_attachment=True)
   abort(404)

if __name__ == '__main__':
   app.run(debug=True)

8 鉤子函數

flask也是面向切面編程的,類似於django 中間件的功能。鉤子函數可用於在每個request前或後,執行一些特殊操作。
常用有以下集中幾種鉤子函數:

鉤子函數 說明
@app.before_first_request 在第一次請求處理之前先被執行
@app.before_request 在每次請求之前都被執行
@app.after_request 在每次請求(視圖函數處理)之後都被執行, 前提是視圖函數沒有出現異常
@app.teardown_request 在每次請求 (視圖函數處理)之後都被執行, 無論視圖函數是否出現異常,都被執行, 工作在非調試模式時 debug=False

參考文獻:
https://www.w3cschool.cn/flask/flask_sending_form_data_to_template.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章