今天來研究一個小小的功能。當咱們進入一個網站,它怎麼判斷我是否是它的用戶?讓用戶登陸唄,若是它能正常登陸,它就是個人用戶唄?你有沒想過它是怎麼判斷我是否是它用戶的?此次就來從先後端來說一講是怎麼來實現這個功能的。css
註冊通常流程能夠簡單的分爲填寫信息,驗證信息,提示用戶,寫入數據庫,註冊成功,大體流程以下圖所示。html
這裏用 JS 完成最簡單的註冊流程,跑通邏輯,實際工做中遠比這複雜。前端
簡化驗證環節,只檢查郵箱是否輸入正確node
首先準備一個最簡單的註冊頁面如,上圖所示。ajax
CSS 這裏有兩個注意點:數據庫
label
和label::after
不一樣字數的文字,兩端對齊label
和input
居中對齊用vertical-align:middle
*{padding:0;margin:0;box-sizing:border-box;} body{ display: flex; justify-content: center; align-items: center; height:100vh; } .sign_in_form{ border:1px solid red; padding:20px; width:400px; } .row{ margin-bottom: 10px; } h1{ text-align: center; } input{ vertical-align: middle; } label{ vertical-align: middle; /*border:1px solid green;*/ width:5em; display: inline-block; height:20px; line-height:20px; overflow: hidden; text-align: justify; } label::after{ content:''; display: inline-block; /*border:1px solid blue;*/ width:100%; }
HTML 文件:編程
<form class="sign_in_form"> <h1>註冊</h1> <div class="row"> <label for="email">用戶名</label> <input type="text" id="email" name="email"> <span class="error"></span> </div> <div class="row"> <label for="password">密碼</label> <input type="password" id="password" name="password"> <span class="error"></span> </div> <div class="row"> <label for="password_confirmation">確認密碼</label> <input type="password" id="password_confirmation" name="password_confirmation"> <span class="error"></span> </div> <div class="row"> <input type="submit" value="註冊"> </div> </form>
server 文件寫一個路由:當咱們訪問首頁時,跳轉頁面(這裏默認跳轉註冊頁面)json
if (path === '/'){ let string = fs.readFileSync('./signUp.html','utf8') response.setHeader('Content-Type','text/html;charset=utf-8') response.statusCode = 200 response.write(string) response.end() }
至此一個簡單的登陸頁面就完成了,當咱們點擊註冊按鈕時,就會像服務器發送一個請求。segmentfault
從上圖中咱們能夠看到,form
表單能夠發送一個GET
,請求體變成查詢參數附在URL
上,這是GET
請求的一個特性,後臺經過讀取查詢參數就能夠獲知請求信息。後端
這裏就產生了一問題,帳戶密碼放在URL
上太不安全了,別人一眼就能看到個人密碼,這樣確定不行。
固然form
表單能夠發起POST
請求,但咱們這裏用ajax
發送請求
let $signInForm = $('.sign_in_form') let userInfoHash ={} $signInForm.on('submit',function(e){ e.preventDefault() let findUser = ['email','password','password_confirmation'] findUser.forEach((key)=>{ let value = $(this).find(`input[name=${key}]`).val() userInfoHash[key] = value }) $.post('/sign_up',hash).then( (response)=>{console.log(response)}, (response)=>{console.log(response)} ) })
當點擊註冊按鈕時,經過findUser
對象提供的key
,找到對應的value
,用戶所填寫的信息,將被保存到userInfoHash
中,經過POST
請求傳遞給服務器。
服務器端作個路由,當我請求路徑爲sign_up
且爲POST
請求,裏面纔會執行。
if(path === '/sign_up' && method === 'POST'){ let body = [] request.on('data',(chunk)=>{ body.push(chunk) }).on('end',()=>{ body = Buffer.concat(body).toString() console.log(body) }) response.statusCode = 200 response.end() }
HTTP
傳送方法是將數據一段一段上傳,因此在服務器端須要分別獲取數據,而後在將他們拼接成一塊兒,轉變成後端須要的字符串。
上面的寫法有個問題——點擊按鈕發送請求後,客戶端一直收不到響應,就會報錯
其實HTTP
傳送的時是一個異步的過程,裏面還沒執行完,外面就已經執行了,這邊能夠用Promise
來解決下這個問題
function readBody(request) { return new Promise((resolve,reject) =>{ let body = [] request.on('data',(chunk)=>{ body.push(chunk) }).on('end',()=>{ body = Buffer.concat(body).toString() resolve(body) }) }) }
readBody
內部返回一個Promise
對象,成功調用resolve
函數,失敗調用reject
函數,這邊就默認它會成功。
因此上面代碼能夠改寫成:
if(path === '/sign_up' && method === 'POST'){ readBody(request).then( (body)=>{ console.log(body) response.statusCode = 200 response.end() }) }
調用readBody
函數後,由於Promise
返回的是一個對象能夠直接在後面用.then()
操做,成功執行前面resolve
函數,失敗執行後面reject
函數,不過這裏要注意,若是真出錯了真正的錯誤信息在第二個.then()
的resolve
函數裏,以下所示:
readBody(request).then( ()=>{console.log('success'), ()=>{console.log('錯誤不執行')}).then( ()=>{console.log('error') })
後端成功拿到數據後,這個數據是字符串的形式,後端須要把它一步步拆解出來。
let bodyArr = body.split('&') let userInfoHash = {} bodyArr.forEach((e)=>{ let part = e.split('=') userInfoHash[part[0]] = decodeURIComponent(part[1]) }) console.log(userInfoHash)
將拆分出來的數據一一對應的保存到 userInfoHash
裏。
從這裏咱們不難看出,前端想盡一切辦法把數據辦成字符串傳給後端,後端在想盡一切辦法把前端傳遞來數據拆分紅能用的格式。
固然了,後臺響應的內容也是,前端拿到也是字符串。
當拿到數據後,應對數據進行驗證,是否符合要求,這裏簡化起見,只驗證email
中有無@
符號與password
和password_confirmation
,若是正確就註冊成功。
response.setHeader('Content-Type','application/json;charset=utf-8') let {email,password,password_confirmation} = userInfoHash if(email.indexOf('@') === -1){ response.statusCode = 400 response.write(`{ "errors":{ "email":"invalid" } }`) }else if(password !== password_confirmation){ response.statusCode = 400 response.write(`{ "errors":{ "password_confirmation":"mismatch" } }`) }else{ response.statusCode = 200 response.write(`{ "success":"success" }`) }
這邊要注意的是@
符號在nodejs
會以%40
的形式出現因此這邊須要對它進行轉碼。
這裏要注意的是後臺提供的響應數據要用json
的形式傳送給前端,若是格式不肯定,前端那邊很難操做,也會形成問題的來源,後臺傳送數據時只需在響應部分加上響應頭response.setHeader('Content-Type','application/json;charset=utf-8')
,前端拿到後會有相應的轉換方法。
$.post('/sign_up',hash).then( (response)=>{ let {success} = response if(success === 'success'){ window.location.herf = '/sign_in' } }, (response)=>{ let {email,password_confirmation} = response.responseJSON.errors if(email === 'invalid'){ $signInForm.find('input[name=email]').siblings('.error').text('郵箱錯誤') }else if(password_confirmation === 'mismatch'){ $signInForm.find('input[name=password_confirmation]').siblings('.error').text('密碼不匹配') } })
根據後臺響應的信息,在頁面中提示用戶相關信息。
其實在用戶提交表單時,前端應該先阻止提交,斷定一下用戶是否填寫正確,在發送請求,這樣在用戶填寫錯誤時候無需發送請求,節約資源。
if(userInfoHash.email === ''){ $signInForm.find('input[name=email]').siblings('.error').text('填郵箱呀') return }else if(userInfoHash.password === ''){ $signInForm.find('input[name=password]').siblings('.error').text('填密碼呀') return }else if(userInfoHash.password_confirmation === ''){ $signInForm.find('input[name=password_confirmation]').siblings('.error').text('確認密碼呀') return }else if(userInfoHash.password !== userInfoHash.password_confirmation){ $signInForm.find('input[name=password_confirmation]').siblings('.error').text('密碼不對呀') return }
前端檢測用戶有沒填寫,若是沒填寫的話,直接提示用戶,無需提交後臺。
註冊成功後,把數據寫入數據庫,這裏要注意,用戶隱私信息不能直接存儲在數據庫裏,這裏爲了學習方便,故直接保存。
咱們建立一個簡單的文件,當作數據庫,數據庫是以哈希表的形式存儲數據
let usersString = fs.readFileSync('./db/db','utf8') //讀取數據庫文件 let usersArr try{ usersArr = JSON.parse(usersString) //轉化成對象 }catch(exception) { usersArr = [] } let isUse = false for(let i = 0; i < usersArr.length; i++){ //遍歷 usersArr if(usersArr[i].email === email){ //若是 usersArr 中存在用戶的郵箱,已經註冊 isUse = true break } } if(isUse){ //若是郵箱存在響應前端操做提示用戶 response.statusCode = 404 response.write(`{ "errors":{ "email":"isUse" } }`) }else{ //若是不存在將註冊信息寫入數據庫 response.statusCode = 200 usersArr.push(userInfoHash) //存入剛剛讀取出來的 usersArr usersArr = JSON.stringify(usersArr) // 轉變成字符串 fs.writeFileSync('./db/db',usersArr) //存入數據庫 response.write(`{ "successes":{ "success":"success" } }`) }
若是前面都層高,最後進入寫數據庫環節:讀取數據庫內容——判斷用戶是否存在(這邊是判斷郵箱)——不存在,寫入數據庫;存在發送響應信息。
至此註冊環節所有結束,這邊要特別注意,數據庫讀取出來的內容是字符串
數據類型在編程中很是重要,剛開始接觸時,老把符合JSON
語法的字符串當成對象,弄清楚數據類型相當重要
if...else
解決不了問題,若是有加一個for
循環console.log
解決不了了bug
,若是有那是console.log
不夠多網站登陸流程可參閱:從先後端分別學習——註冊/登陸流程2