摘要:上面的寫法有個(gè)問題點(diǎn)擊按鈕發(fā)送請(qǐng)求后,客戶端一直收不到響應(yīng),就會(huì)報(bào)錯(cuò)其實(shí)傳送的時(shí)是一個(gè)異步的過程,里面還沒執(zhí)行完,外面就已經(jīng)執(zhí)行了,這邊可以用來解決下這個(gè)問題內(nèi)部返回一個(gè)對(duì)象,成功調(diào)用函數(shù),失敗調(diào)用函數(shù),這邊就默認(rèn)它會(huì)成功。
今天來研究一個(gè)小小的功能。當(dāng)我們進(jìn)入一個(gè)網(wǎng)站,它怎么判斷我是不是它的用戶?讓用戶登錄唄,如果它能正常登錄,它就是我的用戶唄?你有沒想過它是怎么判斷我是不是它用戶的?這次就來從前后端來講一講是怎么來實(shí)現(xiàn)這個(gè)功能的。
注冊(cè)注冊(cè)一般流程可以簡(jiǎn)單的分為填寫信息,驗(yàn)證信息,提示用戶,寫入數(shù)據(jù)庫,注冊(cè)成功,大致流程如下圖所示。
這里用 JS 完成最簡(jiǎn)單的注冊(cè)流程,跑通邏輯,實(shí)際工作中遠(yuǎn)比這復(fù)雜。
簡(jiǎn)化驗(yàn)證環(huán)節(jié),只檢查郵箱是否輸入正確
注冊(cè)頁面首先準(zhǔn)備一個(gè)最簡(jiǎn)單的注冊(cè)頁面如,上圖所示。
CSS 這里有兩個(gè)注意點(diǎn):
label和label::after不同字?jǐn)?shù)的文字,兩端對(duì)齊
label和input居中對(duì)齊用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 文件:
server 文件寫一個(gè)路由:當(dāng)我們?cè)L問首頁時(shí),跳轉(zhuǎn)頁面(這里默認(rèn)跳轉(zhuǎn)注冊(cè)頁面)
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() }
至此一個(gè)簡(jiǎn)單的登錄頁面就完成了,當(dāng)我們點(diǎn)擊注冊(cè)按鈕時(shí),就會(huì)像服務(wù)器發(fā)送一個(gè)請(qǐng)求。
發(fā)起 POST 請(qǐng)求
從上圖中我們可以看到,form表單可以發(fā)送一個(gè)GET,請(qǐng)求體變成查詢參數(shù)附在URL上,這是GET請(qǐng)求的一個(gè)特性,后臺(tái)通過讀取查詢參數(shù)就可以獲知請(qǐng)求信息。
這里就產(chǎn)生了一問題,賬戶密碼放在URL上太不安全了,別人一眼就能看到我的密碼,這樣肯定不行。
當(dāng)然form表單可以發(fā)起POST請(qǐng)求,但我們這里用ajax發(fā)送請(qǐng)求
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)} ) })
當(dāng)點(diǎn)擊注冊(cè)按鈕時(shí),通過findUser對(duì)象提供的key,找到對(duì)應(yīng)的value,用戶所填寫的信息,將被保存到userInfoHash中,通過POST請(qǐng)求傳遞給服務(wù)器。
服務(wù)器端做個(gè)路由,當(dāng)我請(qǐng)求路徑為sign_up且為POST請(qǐng)求,里面才會(huì)執(zhí)行。
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傳送方法是將數(shù)據(jù)一段一段上傳,所以在服務(wù)器端需要分別獲取數(shù)據(jù),然后在將他們拼接成一起,轉(zhuǎn)變成后端需要的字符串。
上面的寫法有個(gè)問題——點(diǎn)擊按鈕發(fā)送請(qǐng)求后,客戶端一直收不到響應(yīng),就會(huì)報(bào)錯(cuò)
其實(shí)HTTP傳送的時(shí)是一個(gè)異步的過程,里面還沒執(zhí)行完,外面就已經(jīng)執(zhí)行了,這邊可以用Promise來解決下這個(gè)問題
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內(nèi)部返回一個(gè)Promise對(duì)象,成功調(diào)用resolve函數(shù),失敗調(diào)用reject函數(shù),這邊就默認(rèn)它會(huì)成功。
所以上面代碼可以改寫成:
if(path === "/sign_up" && method === "POST"){ readBody(request).then( (body)=>{ console.log(body) response.statusCode = 200 response.end() }) }
調(diào)用readBody函數(shù)后,因?yàn)?b>Promise返回的是一個(gè)對(duì)象可以直接在后面用.then()操作,成功執(zhí)行前面resolve函數(shù),失敗執(zhí)行后面reject函數(shù),不過這里要注意,如果真出錯(cuò)了真正的錯(cuò)誤信息在第二個(gè).then()的resolve函數(shù)里,如下所示:
readBody(request).then( ()=>{console.log("success"), ()=>{console.log("錯(cuò)誤不執(zhí)行")}).then( ()=>{console.log("error") })驗(yàn)證數(shù)據(jù)
后端成功拿到數(shù)據(jù)后,這個(gè)數(shù)據(jù)是字符串的形式,后端需要把它一步步拆解出來。
let bodyArr = body.split("&") let userInfoHash = {} bodyArr.forEach((e)=>{ let part = e.split("=") userInfoHash[part[0]] = decodeURIComponent(part[1]) }) console.log(userInfoHash)
將拆分出來的數(shù)據(jù)一一對(duì)應(yīng)的保存到 userInfoHash里。
從這里我們不難看出,前端想盡一切辦法把數(shù)據(jù)辦成字符串傳給后端,后端在想盡一切辦法把前端傳遞來數(shù)據(jù)拆分成能用的格式。
當(dāng)然了,后臺(tái)響應(yīng)的內(nèi)容也是,前端拿到也是字符串。
當(dāng)拿到數(shù)據(jù)后,應(yīng)對(duì)數(shù)據(jù)進(jìn)行驗(yàn)證,是否符合要求,這里簡(jiǎn)化起見,只驗(yàn)證email中有無@符號(hào)與password和password_confirmation,如果正確就注冊(cè)成功。
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" }`) }
這邊要注意的是@符號(hào)在nodejs會(huì)以%40的形式出現(xiàn)所以這邊需要對(duì)它進(jìn)行轉(zhuǎn)碼。
這里要注意的是后臺(tái)提供的響應(yīng)數(shù)據(jù)要用json的形式傳送給前端,如果格式不確定,前端那邊很難操作,也會(huì)造成問題的來源,后臺(tái)傳送數(shù)據(jù)時(shí)只需在響應(yīng)部分加上響應(yīng)頭response.setHeader("Content-Type","application/json;charset=utf-8"),前端拿到后會(huì)有相應(yīng)的轉(zhuǎn)換方法。
$.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("郵箱錯(cuò)誤") }else if(password_confirmation === "mismatch"){ $signInForm.find("input[name=password_confirmation]").siblings(".error").text("密碼不匹配") } })
根據(jù)后臺(tái)響應(yīng)的信息,在頁面中提示用戶相關(guān)信息。
其實(shí)在用戶提交表單時(shí),前端應(yīng)該先阻止提交,判定一下用戶是否填寫正確,在發(fā)送請(qǐng)求,這樣在用戶填寫錯(cuò)誤時(shí)候無需發(fā)送請(qǐng)求,節(jié)約資源。
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("確認(rèn)密碼呀") return }else if(userInfoHash.password !== userInfoHash.password_confirmation){ $signInForm.find("input[name=password_confirmation]").siblings(".error").text("密碼不對(duì)呀") return }
前端檢測(cè)用戶有沒填寫,如果沒填寫的話,直接提示用戶,無需提交后臺(tái)。
存儲(chǔ)數(shù)據(jù)注冊(cè)成功后,把數(shù)據(jù)寫入數(shù)據(jù)庫,這里要注意,用戶隱私信息不能直接存儲(chǔ)在數(shù)據(jù)庫里,這里為了學(xué)習(xí)方便,故直接保存。
我們創(chuàng)建一個(gè)簡(jiǎn)單的文件,當(dāng)做數(shù)據(jù)庫,數(shù)據(jù)庫是以哈希表的形式存儲(chǔ)數(shù)據(jù)
let usersString = fs.readFileSync("./db/db","utf8") //讀取數(shù)據(jù)庫文件 let usersArr try{ usersArr = JSON.parse(usersString) //轉(zhuǎn)化成對(duì)象 }catch(exception) { usersArr = [] } let isUse = false for(let i = 0; i < usersArr.length; i++){ //遍歷 usersArr if(usersArr[i].email === email){ //如果 usersArr 中存在用戶的郵箱,已經(jīng)注冊(cè) isUse = true break } } if(isUse){ //如果郵箱存在響應(yīng)前端操作提示用戶 response.statusCode = 404 response.write(`{ "errors":{ "email":"isUse" } }`) }else{ //如果不存在將注冊(cè)信息寫入數(shù)據(jù)庫 response.statusCode = 200 usersArr.push(userInfoHash) //存入剛剛讀取出來的 usersArr usersArr = JSON.stringify(usersArr) // 轉(zhuǎn)變成字符串 fs.writeFileSync("./db/db",usersArr) //存入數(shù)據(jù)庫 response.write(`{ "successes":{ "success":"success" } }`) }
如果前面都層高,最后進(jìn)入寫數(shù)據(jù)庫環(huán)節(jié):讀取數(shù)據(jù)庫內(nèi)容——判斷用戶是否存在(這邊是判斷郵箱)——不存在,寫入數(shù)據(jù)庫;存在發(fā)送響應(yīng)信息。
至此注冊(cè)環(huán)節(jié)全部結(jié)束,這邊要特別注意,數(shù)據(jù)庫讀取出來的內(nèi)容是字符串
總結(jié)
數(shù)據(jù)類型在編程中非常重要,剛開始接觸時(shí),老把符合JSON語法的字符串當(dāng)成對(duì)象,弄清楚數(shù)據(jù)類型至關(guān)重要
前后端交互——字符串,不管是請(qǐng)求還是響應(yīng),都是字符串
后端與數(shù)據(jù)庫交互——字符串
沒有if...else解決不了問題,如果有加一個(gè)for循環(huán)
沒有console.log解決不了了bug,如果有那是console.log不夠多
網(wǎng)站登錄流程可參閱:從前后端分別學(xué)習(xí)——注冊(cè)/登錄流程2
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/108387.html
摘要:昨天研究了網(wǎng)站的注冊(cè)流程,感興趣的可以看下從前后端分別學(xué)習(xí)注冊(cè)登錄流程今天接著研究注冊(cè)登錄流程之登錄。為解決這個(gè)問題,引入,它是由一組隨機(jī)數(shù)組合的哈希表,當(dāng)用戶登錄成功,本來發(fā)放給用戶,現(xiàn)在變成發(fā)放給用戶。 昨天研究了網(wǎng)站的注冊(cè)流程,感興趣的可以看下:從前后端分別學(xué)習(xí)——注冊(cè)/登錄流程1 今天接著研究注冊(cè)/登錄流程之登錄。 登錄 首先來看一下登陸過程:showImg(https://s...
摘要:用存儲(chǔ)用戶路由守衛(wèi)路由中設(shè)置的字段就在當(dāng)中每次跳轉(zhuǎn)的路徑登錄狀態(tài)下訪問頁面會(huì)跳到如果沒有訪問任何頁面。一個(gè)簡(jiǎn)單的保存登錄狀態(tài)的小。 Vue項(xiàng)目中實(shí)現(xiàn)用戶登錄及token驗(yàn)證 先說一下我的實(shí)現(xiàn)步驟: 使用easy-mock新建登錄接口,模擬用戶數(shù)據(jù) 使用axios請(qǐng)求登錄接口,匹配賬號(hào)和密碼 賬號(hào)密碼驗(yàn)證后, 拿到token,將token存儲(chǔ)到sessionStorage中,并跳轉(zhuǎn)到首...
摘要:其實(shí),該復(fù)雜的東西在哪放都復(fù)雜,只不過現(xiàn)在更清晰一點(diǎn)使用不好的地方就是太繁瑣了,定義各種各種組件。。。。。 之前做了個(gè)好電影搜集的小應(yīng)用,前端采用react,后端采用express+mongodb,最近又將組件間的狀態(tài)管理改成了redux,并加入了redux-saga來管理異步操作,記錄一些總結(jié) 在線地址 手機(jī)模式 源碼 主要功能 爬取豆瓣電影信息并錄入MongoDB 電影列表展示...
摘要:本使用創(chuàng)建本地服務(wù)器,在就能完成全部流程,并不需要線上服務(wù)器。路徑要與后端接口一致。后端返回成功后,前端數(shù)據(jù)中對(duì)應(yīng)的元素也要?jiǎng)h掉,更新視圖??刂破骼锬靡粋€(gè)方法出來說一下吧,完整的代碼都在。讀取操作完成后調(diào)用釋放連接。 寫在前面 本文只是本人學(xué)習(xí)過程的一個(gè)記錄,并不是什么非常嚴(yán)謹(jǐn)?shù)慕坛?,希望和大家一起共同進(jìn)步。也希望大家能指出我的問題。適合有一定基礎(chǔ),志在全棧的前端初學(xué)者學(xué)習(xí),從點(diǎn)擊按鈕...
閱讀 2866·2021-09-10 10:50
閱讀 2214·2019-08-29 16:06
閱讀 3221·2019-08-29 11:02
閱讀 1118·2019-08-26 14:04
閱讀 2834·2019-08-26 13:24
閱讀 2332·2019-08-26 12:16
閱讀 574·2019-08-26 10:29
閱讀 3118·2019-08-23 18:33