模擬註冊登入+cookie
專案的原始碼及使用方法請點選這裡. 執行demo需要先安裝node
這篇文章不會貼上全部程式碼, 只會說下思路. 具體實現還請到github庫中看原始碼.
基本結構
index.html
: 首頁
sign_in.html
: 登入頁面
sign_up.html
: 註冊頁面
server.js
: 伺服器
db/users
: 模擬資料庫, 實質上是一個數組, 每個陣列項是一個物件, 表示一個使用者
使用者可以直接進入首頁, 通過登入的方式進入首頁. 通過登入的方式進入首頁時設定了cookie
, 此時可以再首頁看到使用者的郵箱.
使用者註冊的賬號會儲存在db/users
檔案中. 當用戶以相同的郵箱註冊時會提示使用者已註冊. 註冊時也會驗證郵箱的有效性和密碼的一致性.
伺服器主要實現路由操作. 當使用不同的路由時採取不同的動作.
首頁index.html
由於是模擬登入, 因此首頁只會展示使用者的郵箱(通過cookie
). 若沒有cookie
, 則不顯示郵箱
<body>
<h1>歡迎 __user__ 登入</h1>
</body>
__user__
是一個佔位符, server.js
程式在後臺執行. 當用戶登入時, server.js
設定__user__
為使用者郵箱
string = string.replace('__user__', foundUser.email) //foundUser為登入使用者
註冊頁sign_up.html
我們在進行註冊的時候需要輸入密碼, 而且還要確認這個密碼. 因此結構如下:
<form action="" id="signUpForm">
<div class="row">
<label for="">郵箱</label>
<input type="text" name="email">
<span class="message"></span><!--錯誤處理佔位-->
</div>
<div class="row">
<label for="">密碼</label>
<input type="password" name="password">
<span class="message"></span>
</div>
<div class="row">
<label for="">確認密碼</label>
<input type="password" name="password_confirmation">
<span class="message"></span>
</div>
<div class="row">
<input type="submit" value="註冊">
</div>
</form>
對於郵箱, 密碼等我們需要進行檢驗, 比如:
- 郵箱是否有效, 有
@
符 - 郵箱是否未註冊.
- 兩次密碼是否輸入一致
- 全部都是必填項.
若全部複合條件, 則使用post
方法向伺服器傳送請求.
//假設已經引入jQuery庫
$form = $('#signUpForm')
$form.on('submit', function(e){
e.preventDefault()
let hash = {}
let need = ['email', 'password', 'password_confirmation']
need.forEach(function(name){
let value = $form.find(`[name=${name}]`).val()
hash[name] = value
})
$form.find('.message').each(function(index, span){
$(span).text('')
})
if(hash['email'] === '') {
$form.find('[name="email"]').siblings('.message').text("請填寫郵箱")
return
}
if(hash['password'] === '') {
$form.find('[name="password"]').siblings('.message').text("請填寫密碼")
return
}
if(hash['password_confirmation'] === '') {
$form.find('[name="password_confirmation"]').siblings('.message').text("請填寫密碼")
return
}
if(hash["password"] !== hash['password_confirmation']) {
$form.find('[name="password_confirmation"]').siblings('.message').text("密碼不匹配")
return
}
$.post('/sign_up', hash)
.then(function(response){
let message = response
console.log(message)
if (message.email && message.email === 'successed') {
$form.find('[name="email"]').siblings('.message')
.text('註冊成功')
}
}, (xhr)=>{
console.log(xhr)
//伺服器端設定了application/json頭
let {message}=xhr.responseJSON
console.log(message)
if (message.email && message.email === 'invalid') {
$form.find('[name="email"]').siblings('.message')
.text('郵箱格式錯誤')
} else if (message.email && message.email === 'used') {
$form.find('[name="email"]').siblings('.message')
.text('郵箱已被註冊')
}
})
})
$.post
方法後面可以接Promise
的方法來根據伺服器返回的資料(JSON
)顯示結果
登入頁sign_in.html
這個頁面時登入頁面, 因此只需要郵箱和密碼就可以了
<form action="" id="signInForm">
<div class="row">
<label for="">郵箱</label>
<input type="text" name="email">
<span class="message"></span>
</div>
<div class="row">
<label for="">密碼</label>
<input type="password" name="password">
<span class="message"></span>
</div>
<div class="row">
<input type="submit" value="登入">
</div>
</form>
當然還是需要通過server.js
返回的資料進行驗證:
- 賬戶是否存在
- 密碼是否正確
- 全部為必填項
//假設已經引入jQuery
$form = $('#signInForm')
$form.on('submit', function(e){
e.preventDefault()
let hash = {}
let need = ['email', 'password']
need.forEach(function(name){
let value = $form.find(`[name=${name}]`).val()
hash[name] = value
})
$form.find('.message').each(function(index, span){
$(span).text('')
})
if(hash['email'] === '') {
$form.find('[name="email"]').siblings('.message').text("請填寫郵箱")
return
}
if(hash['password'] === '') {
$form.find('[name="password"]').siblings('.message').text("請填寫密碼")
return
}
$.post('/sign_in', hash)
.then(function (response){
window.location.href='/'
}, ()=>{
alert('郵箱或密碼錯誤')
})
})
伺服器server.js
server.js
主要實現了以下路由
if (path === '/') {
//返回index.html, 若是使用者登入則獲取已設定好的cookie
} else if (path === '/sign_up' && method === 'GET') {
//返回註冊頁
} else if (path === '/sign_up' && method === 'POST') {
//獲取註冊頁的post請求體(使用者註冊資訊), 根據請求體返回響應JSON資料
} else if (path === '/sign_in' && method === 'GET') {
//返回登入頁
} else if (path === '/sign_in' && method === 'POST') {
//獲取登入頁的post請求體(使用者登入資訊), 根據請求體返回JSON資料, 且設定cookie
}
因為請求體不是一次性返送的, 每次傳給伺服器的資料大小有要求, 因此我們需要伺服器接收完整的請求體才能做出下一步動作. 在server.js
中實現完整讀取請求體的函式readBody
.
function readBody(request) {
return new Promise(function (resolve, reject){
let body = [] //請求體
request.on('data', function(chunk){
console.log('伺服器正在接收請求體')
// 監聽data事件
body.push(chunk);
}).on('end', function(){
// 監聽end事件, 當伺服器接收POST請求中的全部資料觸發
console.log('伺服器接收完畢請求體')
body = Buffer.concat(body).toString();
resolve(body)
})
})
}
我們已經知道server.js
的大致架構. 比較重要的是兩個處理, 一個是使用者註冊時的處理, 一個是使用者登入時的處理.
使用者註冊
當用戶進行註冊時, sign_up.html
首先會自己檢測必填項是否已填寫. 然後通過post
方法將使用者填入的郵箱和密碼資訊傳送給server.js
伺服器. 伺服器會一次進行以下工作:
- 獲取請求體(郵箱, 密碼), 注意要解碼
- 郵箱檢測(
@
符號)及密碼一致性檢驗(即兩次密碼是否相同) - 遍歷
db/users
檔案, 檢測是否未註冊 - 返回響應, 若已註冊通知使用者重新填寫, 否則告訴使用者註冊成功
當註冊成功時, 資料會寫入db/users
檔案.
假設我們註冊的是[email protected]
, 密碼為:1
. 那麼我們就會在db/users檔案中看到如下賬號:
[{"email": "[email protected]", "password": "1"}] //注意users的內容是個陣列, 每個陣列項是物件, 代表一個賬戶
使用者登入
假設使用者已經註冊好賬戶了, 在登入頁sign_in.html
輸入郵箱([email protected]
)和密碼(1
). 點選登入會做一下事情:
- 檢查必填項
- 向伺服器傳送響應
- 遍歷
db/users
檔案, 檢測是否存在使用者 - 返回響應, 若不存在則告訴使用者錯誤, 否則先設定
cookie
再轉向登入頁index.html
index.html
讀取請求體中的cookie
資訊並顯示出來
當直接進入首頁index.html
時, 不會顯示cookie
資訊.
cookie
cookie上文中簡單提到了. 使用者登入時會設定cookie, 登入成功讀取cookie值並顯示.
當點選sign_in.html頁面的登入按鈕時, , server.js伺服器會將設定cookie
response.setHeader('Set-Cookie', `sign_in_email=${email}`) //設定email的cookie
伺服器將設定的cookie返回給瀏覽器, 因為郵箱及密碼沒有問題則再發送一次包含cookie的請求, 這次請求是告訴伺服器我要進入登入頁.
登入頁會取出請求體中的cookie並顯示.
let cookies = request.headers.cookie.split('; ') //獲取請求體中的cookie並分割
let hash = {}
for (let i = 0; i < cookies.length; i++) {
let parts = cookies[i].split('=')
let key = parts[0]
let value = parts[1]
hash[key] = value//hash中存放的是key-value形式的cookie
}
//hash中存在登入賬戶的cookie
let email = hash.sign_in_email
let users = fs.readFileSync('./db/users', 'utf8')
users = JSON.parse(users)
let foundUser
for (let i = 0; i < users.length; i++) {
if (users[i].email === email) {
foundUser = users[i]
break
}
}
// 將佔位符__user__換成cookie值. foundUser是登入賬戶的cookie
string = string.replace('__user__', foundUser.email)
當用戶直接進入index.html頁面而不是通過登入方式進入時, 伺服器就不會設定cookie. 此時就不顯示cookie, 給佔位符一個統一的名稱即可.
string = string.replace('__user__', '同學')