1. 程式人生 > >模擬註冊登入+cookie

模擬註冊登入+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__', '同學')