又LAG隨性筆記
  • 關於我
  • 作品集
  • 生活隨筆
  • 與我聯絡
  • 隨手扎

隨手扎

October 9, 2022

你可能不知道的HTTP Header--If-Match和該怎麼設計Web API

前言

問個 假如我今天有個表單在前端要送,然後我想要先檢查這個表單有沒有被其他人更新

這是在一次和朋友的討論中有人提出來的問題。

恩…這是很典型資料競爭的問題,但並不是多個執行緒存取一份資料,而是多個瀏覽器客戶端(Browser Client)存取同份伺服器資源。

通常,我們在討論RESTful - 一種當下很常見的Web API設計方式的時候,只會提到建立(Create / POST)、讀取(Read / GET)、更新(Update / PUT)、補充(PATCH)和刪除(DELETE),也就是一般的CRUD。甚少會在深度討論一些細節,就如開頭提到的問題。並且通常在瀏覽網站,也很少去注意各個網站的API設計方式。

那麼這個問題要怎麼解決呢?

不管怎麼說,伺服器上的資料正確性最終需要由伺服器處理保護。 可以簡單一些在資料上添加版本(Version)或最後修改時間(Last Update time)的欄位。當收到更新或刪除訊息,該欄位應該保持一致。不過這就會出現一個很奇妙的現象:

> GET /data1


< 200 OK
< Content-Type: application/json
< 
< {"name": "Bob", "age": 18, "version": 1}

當我們有一個資料data1其版本為1,如果我們需要更新的話,下面的訊息代表什麼意思?

> PUT /data1
> Content-Type: application/json
> 
> {"name": "Alice", "age": 18, "version": 1}


< 204 No Content

這是一個更新,版本是不是應該跳到2?但是按照前述這個版本應該要與記錄一致才會被修改,但是這樣似乎又與PUT的意圖衝突?

或許有一些設計是檢查更新版本-1是不是與當前記錄版本相同作為檢查判斷條件。但是想想,其實版本號的決定,似乎不該由Client傳進的資料決定,而應該由後端資料管理行為決定。

那麼來介紹另一種設計方式,其實HTTP一般性規範都幫你準備好了,只是在我經驗觀察上,似乎並沒有那麼易用(也可能是因為了解的人真的不多),並不經常看到這樣的設計。這可能會包含一些你可能不知道的HTTP狀態碼(HTTP Status Code)和HTTP Headers。

October 8, 2022

你可能不知道的JS自動型別轉換

前言

如果要針對一個 物件陣列 取最大值該怎麼做?

一天,一位同事這麼問到。

首先需要先知道的是一般在JavaScript物件是無法比較大小的,所以這句話的意思是將物件特定屬性作為比較參考值,或是將物件數個屬性計算成一個可以比較的值後作為參考值,再進行比較尋找最大值。

起初,針對這個問題我第一想到的就是三種方式:

  1. 使用Array.prototype.reduce()
  2. 使用Array.prototype.sort()
  3. 使用Math.max()

最後一個你可能會很好奇:物件無法比較,並且Math.max()又不像Python的max()可以輸入key函式,這樣可以比較嗎?

沒錯,這個方式是存在問題的,但原因並不是因為「物件無法比較」,而是結果型別的問題,這個問題之後會提到。並且這也引發了今天的議題–「JS自動型別轉換」。

物件陣列怎麼取最大值?

這並不是今天主題的重點,也就不賣關子了,直接給出做法。

同樣拿Person類別為例子:

class Person {
    name = "";
    #birthday = new Date(); // 私有屬性
    get birthday() { return this.#birthday }; // 調整處
    addr = ""
    
    get age() {
        return new Date().getYear() - this.birthday.getYear();
    }
    
    constructor(name, birthday, addr = "") {
        this.name = name;
        this.#birthday = birthday; // 調整處
        this.addr = addr;
    }
    
    
    hello() {
        console.log(`Hello, ${this.name}.`);
    }
}

現在有10個年幼不依的人:

var people = [
    "Alice",
    "Bob",
    "Candy",
    "Danel",
    "Frank",
    "Grant",
    "Harry",
    "Iris",
    "Joe",
    "Kevin"];

people = people.map(name => new Person(name, new Date(Math.floor(Math.random() * 100) + 1990, 1), ""))

現在如果希望找到年紀最大的,這裡提供幾種方式:

October 7, 2022

你可能不知道的JS物件私有屬性

前言

前幾年我曾經寫過「7天搞懂JS進階議題」系列文章,你可以在我的網站或是CoderBridge閱讀。其中在番外篇提到過「隱私成員」,在當時因為JavaScript並沒有隱私屬性的設計,所以想實現,當時使用了閉包和屬性描述器來處理。

時過境遷,在我寫完發表沒多久,ES11(2020)也正式推出了,其中就有關於私有屬性和私有方法的設計。根據Can I Use使用支援程度已經超過九成,也就是在今天主要瀏覽器除了一些舊版本和特別的瀏覽器外應該多數也都支援了。

原先JavaScript設計的問題

class Person {
    name = "";
    birthday = new Date();
    addr = ""
    
    get age() {
        return new Date().getYear() - this.birthday.getYear();
    }
    
    constructor(name, birthday, addr = "") {
        this.name = name;
        this.birthday = birthday;
        this.addr = addr;
    }
    
    
    hello() {
        console.log(`Hello, ${this.name}.`);
    }
}

var bob = new Person(/* name = */ "Bob", 
                     /* birthday = */ new Date(2004/* year */, 
                                               1 /* mouth */, 
                                               1 /* day */),
                     /* addr = */ "臺灣")

console.log(`${bob.name}今年${bob.age}歲`); // Bob今年18歲
bob.hello();

bob.birthday = new Date();  // 變更生日
console.log(`${bob.name}今年${bob.age}歲`); // Bob今年0歲

如果設計了一個類別Person有名字(name)、生日(birthday)、地址(addr)等屬性,從嘗試性來說生日在出生以後就不能夠變了,但是上面程式碼片段當我們建立一個實例物件bob後,依然可以變更生日。

October 6, 2022

你可能不知道的WebAuthN(FIDO)

名詞解釋

AuthN 和 AuthZ 分別是 Authentication 和 Authorization 的簡寫,也就是驗證和授權。

不光兩個英文字像…簡寫形式一樣容易讓人混淆🐷
不過比起全名或是 A12n 或 A11n ,這樣好分辨多了Orz…

那麼 WebAuthN 就是 Web 和 AuthN 的結合,也就是在 Web 上的身份驗證。這裡特別指的是由FIDO聯盟推出的FIDO2的其中一部分。特徵就是讓使用者在瀏覽器瀏覽網頁時,可以利用辨識、臉部辨識等方快速登入。至於FIDO是Fast IDentity Online的縮寫,也就是「快速線上身份識別」,也就不難理解聯盟成立的目的為何。

身份驗證的方式

在之前系列「用Keycloak學習身份驗證與授權」–淺談身份驗證與授權(1)和再談身份驗證與授權中,將整個身份驗證、授權到取得資源處理業務邏輯分成幾個部分看。

這次主要談的是WebAuthN,也就是身份驗證這一塊。能夠證明身份的方式通常又分成這麼幾種:

  • 只有你知道。像是密碼、一次性密碼(OTP)、簡訊驗證碼等等。
  • 只有你持有。像是汽機車鑰匙、硬體金鑰、手機手錶(手機或智慧手錶在附近自動解鎖)。
  • 只有你天生是。指紋、虹膜、DNA。
October 5, 2022

你可能不知道cookie是怎被保存、保存在哪裡?

前言

再來是cookie最後,也是我認為最有意思的一部分–「cookie是怎被保存、保存在哪裡」 。

服務後端

對於開發後端服務的軟體開發人員,自己應當知道cookie存在哪裡,如何才可以將HTTP Request的狀態保留對應下來。而一般前端人員也不需要很清楚的知道cookie儲存在哪裡,只要認為儲存在瀏覽器內或記憶體內就好。

瀏覽器外存在哪裡

但是重開機後有一些cookie也存在,這表示必定有一個檔案儲存地方儲存這cookie。

以Google Chrome在Windows平臺為例,Cookie可能儲存在%LocalAppData%\Google\Chrome\User Data\Default\cookies或%LocalAppData%\Google\Chrome\User Data\Default\Network\cookies,這要看是什麼版本的若有在更新目前應該是後者。

這是以SQLite作為檔案型資料庫儲存的,因此可以使用對應的工具打開來看看:

October 4, 2022

你可能不知道cookie有些只能走特定道路(HTTP)

回信

當瀏覽器收到並儲存cookies後,在下一次的request就會將cookies跟著帶著送出去,回到Server。對於無狀態的HTTP,就可以使用這樣的方式讓Server回憶這個Request之前是誰,做到保留狀態。

同樣地接者調整前篇的程式碼片段。這次要將Server收到的Cookies直接作為Response的內容返回。先來添加需要的package:

from fastapi.requests import Request

然後添加一個endpoint–GET /cookies,將Server收到的Cookies直接作為Response的內容返回:

@app.get('/cookies')
def cookies(request: Request):
    return request.cookies

現在瀏覽 http://127.0.0.1.nip.io:8000/cookies 就可以收到傳給Server的Cookies作為JSON返回。

AJax with credition

  • ««
  • «
  • 1
  • 2
  • 3
  • 4
  •  … 
  • 34
  • »
  • »»
© 又LAG隨性筆記 2025