Day 19 Flask Cookie

讲完前端之後,就一定要说到 Cookie 跟 Session 这两个东西了,这两个是什麽东西呢?又能干什麽呢?

开始之前,首先要先说到 http 的无状态特性。举个例子,现在在逛网拍,想要把某个商品加入购物车,要入一遍帐号密码;接着要送出订单,又要输入一遍帐号密码;不管做什麽都需要输入一遍帐号密码,为什麽会这样呢?因为 http 没有纪录状态。为了解决这样的问题,所以就出现了 Cookie 跟 Session。

Cookie

首先先来说 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)
  1. key: Cookie 名称。必填
  2. value: Cookie 值。
  3. max_age: 多久後过期(在梦中有人跟我说并不是全部的浏览器都支持,我也不知道)。
  4. expires: 什麽时候过期。跟上一个很像,但上一个是填秒数,过了多久後就过期;这个是填时间,什麽时候过期。
  5. path: 可以存取这个 Cookie 的路径(路径是啥?就是 url 扣掉网域剩下的)。
  6. domain: 可以存取这个 Cookie 的网域。
  7. secure: 如果为 True,Cookie 只会在 Https (有 s 时)时才会被传送。
  8. httponly: 如果为 True,JavaScript 无法取得这个 Cookie。
  9. samesite: 可设为 Strict、Lax 和 None,最严格的传输 Cookie 到 最不严格的传输 Cookie。

如果现在多了一个页面需要使用到 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 吧。

大家掰~掰~


<<:  载入页面,什麽时候发 API 适合?

>>:  Day 19 Docker Compose 操作指令

30天学会C语言: Day 14-全部包轨!

struct() 用於建立自定义资料结构的命令,其中可以包含多个不同型别的成员,就像是把多个不同型别...

【Day 12】Python os._exit()和 sys.exit()

Python的程序有2种退出方式:os._exit(), sys.exit() os._exit()...

Day8-安装Kind要在docker之後

从上一章了解各种K8s的特点,在这章将会教学如何安装Kind。 由於其利用docker的特性,会比使...

不是使用专用的、标准化的设备清理命令的清除方法:消磁(Degaussing)

清理方法(The sanitization method),清除(purge),将使数据恢复不可行,...

[Day16] 学 Reactstrap 就离 React 不远了 ~ 用 Tooltips 认识 useEffect

前言 昨天文章有提到在 Tooltips 看到有趣的范例, 有用到 useEffect, 不过我有将...