[Golang] Build A Simple Web Service part.6 — Sign up, Login & Logout with React.js

Jen-Hsuan Hsieh (Sean)
A Layman
Published in
13 min readFeb 4, 2019
Copy right@A layman

Introduction

這篇的學習目標是Golang的註冊,登入及登出。

過程中會先複習到Cookie,Session,Web storage的特性; 前端將使用React開發並用上SPA,以及用AJAX在操作cookie應注意的事情,後端仍用Golang; 最後串起在註冊,登入及登出雙方向的互動。

實作將會以[Golang]Build A Simple Web Service系列以及[React.js] Build An React App系列為基礎,依舊將API server跟web page server分開,以保留開發時的自由度,因此在cookie的操作上將是cross domain的。

Cookie

HTTP的特性之一是stateless(無狀態),網景公司發明cookie繞過無狀態而使HTTP更好使用,但也造成了一些問題。

讓我們回顧cookie在瀏覽器中的特性如下:
1.client端的資料有變動時,藉由寫入cookie可以知道使用者做了哪些動作。
2.cookie是用domain name區分 (domain specific),在任何時機點只要對該domain的server發送request,對應的cookie會被一併夾帶到server。
3.cookie在HTTP中是明文傳送,有安全性的疑慮。
4.cookie的形式為key-value pair,並可以設定expired time以指定其存活時間。

當server收到帶有cookie的request時將會將帶有cookie的response回去。因此也可以在server side為瀏覽器設定cookie。

Session

session是server side儲存使用者資訊的機制。

剛才有提到,server side有能力為瀏覽器設定cookie。使用場景是server會將使用者的uuid放到cookie中,因此如果使用者曾經登入過,當傳到server時,若cookie中的uuid在session中確實有對應的使用者,則可以視為已登入過。

以下是session儲存的地方以及其優缺點:
1.內存: 可能會造成memory leak。
2.緩存: 是實務上最常採用,像是Redis,Memcache。
3.cookie: cookie會隨之變大,而導致request的資料量變大。
4.Database: 可能會造成讀取相當頻繁。

Other storage: Local storage and Session storage

除了cookie外,可以儲存client端資料的還有 web storage,這是HTML5時加入的新技術,可細分為local storage及session storage兩種,三者差異如下:

1.cookie: 大小大約4kb,關閉瀏覽器後會被清除,client side跟server side都可以設定cookie。
2.local storage: 大小大約5MB,將會被永久保存,需要被清除(explicitly deleted),但僅存在於client side中,不參與跟server side的通訊
3.session storage:大小大約5MB,關閉瀏覽器或是頁面都會清除,但僅存在於client side中,不參與跟server side的通訊

用cookie可以追蹤使用者的行為,然而現在歐洲國家有GDPR使用cookie時都必須要提示consent給使用者,使用者也必須同意。 web storage由於無法參與與server side的通訊,因此無法追蹤使用者的行為。

Copy right@A layman

workflow

接著要說明的是web page server與API server的workflow,以Sign up為例:

Copy right@A layman
  • Web page的行為:
    使用者訪問/signup頁面並送出使用者資料時,頁面會透過AJAX呼叫 API server的/signup endpoint,設定session並儲存使用者資料,會再透過AJAX呼叫call API server的/user endpoint以獲得使用者資料,並更新狀態React component的state,此時會重新rendering頁面,用User component取代Signup component。
  • API server的行為:
    1.檢查使用者的名字是否被註冊過了。
    2.建立新的Session及替clien side設定cookie。
    3.將使用者資訊存到database (這裡是用內存當範例)。

Login的workflow:

Copy right@A layman
  • Web page的行為:
    使用者訪問/login頁面並送出資料時,頁面會透過AJAX呼叫 API server的/login endpoint,設定session並儲存使用者資料,會再透過AJAX呼叫call API server的/user endpoint以獲得使用者資料,並更新狀態React component的state,此時會重新rendering頁面,用User component取代Signup component。
  • API server的行為:
    1.檢查使用者的名字是否被註冊過了。
    2.建立新的Session及替clienside設定cookie。

Logout則是清除sessin及使用者的cookie。

Implementation

Web page server and React components

Copy right@A layman

Web page server的架構是延續[React.js] Build An React App part.2- Material-UI, SPA and React Router. web page server為SPA,採用Node.js,僅提供一個entry point且有三個頁面:
1. /Sign up: 提供新註冊用的表單
2. /Login: 提供登入用的表單。
3. /User: 登入成功後跳轉的頁面 + Log out按鈕。

檔案結構將會如左圖所示, 多了Register.js,Login.js及User.js。

Main page component

要注意的事有:

  • 用react-dom-router的Route來控制路由,並用render來加入條件判斷,並在child components裡更新parent component的state.hasLogin。
  • 在componentDidMount時檢查cookie,有cookie則詢問API server此使用者是註冊過,若有則親自改寫state.hasLogin (使用js-cookie)。

MainPage.js的程式碼如下:

Fetch in React

關於React中使用Fetch使用方法,先前在此篇文章已經有簡單介紹過: [Golang] Build A Simple Web Service part.5 — Communicate with the ReactJS page, 這裡再記錄一些細節。

Fetch的Request參數比較重要的是mode跟credentials,mode可分為same-origin/cors/cors-with-forced-preflight/no-cors; 而credentials可分為include/omit/same-origin。

Fetch的Response類型也有分成三種, 分別是basic/cors/opaque,basic可以拿到response的所有資料,cors除了Cache-Control,Content-Language,Content-Type,Expires,Last-Modified和 Progma之外的資料都拿不到,如果是opaque則基本上拿不到任何有用的資料。

在跨域操作Cookie時,需要將mode設為no-cors及將credentials設為include,而這樣得到的Response類型會是opaque。

Sub page component

需要新增Register.js,Login.js及User.js。

Register.js的程式碼:

Login.js的程式碼:

User.js的程式碼:

API server

API server的架構是延續[Golang] Build A Simple Web Service part.5 — Communicate with the ReactJS page的專案。API server提供3個end point:
1. /Sign up: 新增新的session並將使用者資料寫到database (或內存)
2. /Login
3. /Logout

新用到的package將會有:

程式碼只要修改routes/webService.go的內容,此範例用內存代替database以簡化程式碼,並新增兩個global variable: dbSessions以存放當前的session,dbUsers存放註冊過的使用者資料。

getUser()會回傳JSON格式的資料,將Golang struct封裝成JSON格式回傳有兩種方式,一種是用json.Marshall, 寫法如下:

第二種是用json.NewEncoder.encode,也是這次的範例所採用的方式,routes/webService.go的程式碼如下:

Summary

下圖為Sign up的頁面(使用者登入前):

Copy right@A layman

使用者登入後的頁面:

Copy right@A layman

在Golang及React都有一些累積後,逐漸進到前後端互動的操作,登入,登出,註冊,這篇牽涉到的範圍有點廣,盡可能地將這篇整理地收斂一些,若有不清楚的地方建議您可以參考Related topics或是Reference。

謝謝你耐心地讀到Summary,我是Sean HS, 是位軟體工程師。
這片文章是我在研究過程時的筆記,若有錯誤之處,期待您的見解,與您交流討論。

--

--

Jen-Hsuan Hsieh (Sean)
A Layman

Frontend Developer🚀 Angular • React • Nest • Electron • Micro-frontend • Monorepo Architecture • https://daily-learning.herokuapp.com/