今天来研究一个小小的功能。当咱们进入一个网站,它怎么判断我是否是它的用户?让用户登陆呗,若是它能正常登陆,它就是个人用户呗?你有没想过它是怎么判断我是否是它用户的?此次就来从先后端来说一讲是怎么来实现这个功能的。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