講完前端之後,就一定要說到 Cookie 跟 Session 這兩個東西了,這兩個是什麼東西呢?又能幹什麼呢?
開始之前,首先要先說到 http 的無狀態特性。舉個例子,現在在逛網拍,想要把某個商品加入購物車,要入一遍帳號密碼;接著要送出訂單,又要輸入一遍帳號密碼;不管做什麼都需要輸入一遍帳號密碼,為什麼會這樣呢?因為 http 沒有紀錄狀態。為了解決這樣的問題,所以就出現了 Cookie 跟 Session。
首先先來說 Cookie,Cookie 就像是麥X勞的甜心卡,上面有著資訊(買A送B),甚至會有期限(2021/12/31),並且由商家交給顧客自行留存,且只能在原商店使用。
http 中的 Cookie 也是這樣,有 key-value 、期限,同樣由 Server 送給瀏覽器保存,也只能在相同的 Domain(網域) 使用。優點當然是解決了 http 無狀態造成的問題;不過缺點則是 Cookie 有可能會被竄改,所以只適合紀錄一些不重要的數據(話說根據瀏覽器不同,有不同的大小及數量的限制。標準^[1]上是每個 Cookie 4096Bytes,最少每個 Domain 要可以有 20 個 Cookie)。
好了,大概了解完 Cookie 了,那麼就來看一下 Flask 中如何設置 Cookie 吧!讓我們用繼續使用前面的架構,新增幾個檔案:
ithome
├── static
│ └── logo.svg
├── templates
│ ├── res
│ │ ├── home.html
│ │ └── login.html # 新增它
│ ├── base.html
│ ├── index.html
│ └── page_not_found.htmlindex.html
├── app.py
├── configs.py
├── Pipfile
└── Pipfile.lock
舉個例子,假設現在要做個登入後要在 Cookie 中設定 username 並回傳登入後頁面,可以這樣寫:
login.html
{% extends 'base.html' %}
{% block title %}
template value
{% endblock %}
{% block img %}
<img src={{ url_for('static', filename='logo.svg' ) }} />
{% endblock %}
{% block content %}
<h1>Hello</h1>
<form action={{ url_for('login') }} method="POST">
<fieldset>
<legend>Login</legend>
<label>Username: </label>
<input type="text" name="username" /><br />
<label>Password: </label>
<input type="password" name="password" /><br />
<input type="submit" value="Login" />
</fieldset>
</form>
{% endblock %}
app.py
from flask import redirect, request, make_response, render_template
@app.route('/')
def index():
return render_template('res/index.html')
@app.route('/home', methods=['GET'])
def home():
if 'username' in request.cookies:
user = request.cookies.get('username')
else:
user = None
return render_template('res/home.html', username=user)
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET': # 輸入網址會進到這裡
response = make_response(render_template('res/login.html'))
elif request.method == 'POST': # 表單送出後會到這裡
account = request.values.get('username', None)
# 驗證是否有這個使用者以及密碼是否正確,生出驗證結果 auth_result
auth_result = 'success' # 假設成功
''' 建立回應 '''
if auth_result == 'success': # 如果都正確
response = make_response(redirect(url_for('home')))
''' 設定 Cookie '''
response.set_cookie('username', account)
else: # 如果錯誤
response = make_response(redirect(url_for('login')))
else:
response = make_response(redirect(url_for('index')))
return response
如果實際跑一遍就會像這樣:
這樣就確定有放上去了。
Cookie 要包在回傳的東西裡面,在回傳給使用者的瀏覽器設,所以需要使用 Day 19 的 make_response
包進去後再回傳。
看到這裡應該已經會基礎的設定 Cookie 了吧,讓我們仔細的看一下還可以設定什麼,set_cookie
所有可以設定的參數如下:
set_cookie(key, value='', max_age=None, expires=None, path='/', domain=None, secure=False, httponly=False, samesite=None)
s
時)時才會被傳送。如果現在多了一個頁面需要使用到 username,要從 Cookie 取得怎麼辦。讓我們在做一個頁面來實測一下。同樣是使用相同的架構,不過又新增了一個檔案:
ithome
├── static
│ └── logo.svg
├── templates
│ ├── res
│ │ ├── home.html
│ │ ├── index.html
│ │ ├── login.html
│ │ ├── page_not_found.html
│ │ └── settings.html # 新增它
│ └── base.html
├── app.py
├── configs.py
├── Pipfile
└── Pipfile.lock
又舉個例子,假設現在要做個登入後使用者要可以有個人設定的頁面,可以這樣寫:
settings.html
{% extends 'base.html' %}
{% block title %}
template value
{% endblock %}
{% block img %}
<img src={{ url_for('static', filename='logo.svg' ) }} />
{% endblock %}
{% block content %}
{% if username %}
<h1>{{ username }}'s settings</h1>
{% else %}
<a href={{ url_for('index') }}><button>Index</button></a>
{% endif %}
{% endblock %}
@app.route('/settings', methods=['GET'])
def settings():
if 'username' in request.cookies:
user = request.cookies.get('username')
else:
user = None
return render_template('res/settings.html', username=user)
如果已經登入過了(因為這邊沒有設過期時間,所以 Cookie 會一直在,可以手動刪除它),Cookie 的 username 還在,那麼 URL 後面直接改成 settings
就可以跳過去了(雖然可以弄一個 link,直接點就過去了),這樣就可以抓到 Cookie 的值了。
如果要設定時間可以使用 datetime.datetime.now() + datetime.timedelta(<units>=<number>)
這個方式設定。
最後說一下如何刪除,Cookie 如果沒有設定時間就會是關閉瀏覽器的時候刪除,有設定時間不會是那個時間刪除,而是要重新設定一次過期的時間才會被刪除喔。
^[RFC 2965 HTTP State Management Mechanism #5.3]RFC 2965 HTTP State Management Mechanism #5.3
那麼就大概這樣,今天本來打算一次講完 Cookie 跟 Session 的,但是發現講不完,所以下一篇再講 Session 吧。
大家掰~掰~