资讯专栏INFORMATION COLUMN

从前后端分别学习——注册/登录流程1

Gemini / 2300人阅读

摘要:上面的写法有个问题点击按钮发送请求后,客户端一直收不到响应,就会报错其实传送的时是一个异步的过程,里面还没执行完,外面就已经执行了,这边可以用来解决下这个问题内部返回一个对象,成功调用函数,失败调用函数,这边就默认它会成功。

今天来研究一个小小的功能。当我们进入一个网站,它怎么判断我是不是它的用户?让用户登录呗,如果它能正常登录,它就是我的用户呗?你有没想过它是怎么判断我是不是它用户的?这次就来从前后端来讲一讲是怎么来实现这个功能的。

注册

注册一般流程可以简单的分为填写信息,验证信息,提示用户,写入数据库,注册成功,大致流程如下图所示。

这里用 JS 完成最简单的注册流程,跑通逻辑,实际工作中远比这复杂。

简化验证环节,只检查邮箱是否输入正确

注册页面

首先准备一个最简单的注册页面如,上图所示。

CSS 这里有两个注意点:

labellabel::after不同字数的文字,两端对齐

labelinput居中对齐用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 文件写一个路由:当我们访问首页时,跳转页面(这里默认跳转注册页面)

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()
}

至此一个简单的登录页面就完成了,当我们点击注册按钮时,就会像服务器发送一个请求。

发起 POST 请求


从上图中我们可以看到,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传送方法是将数据一段一段上传,所以在服务器端需要分别获取数据,然后在将他们拼接成一起,转变成后端需要的字符串。

上面的写法有个问题——点击按钮发送请求后,客户端一直收不到响应,就会报错

Promise

其实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中有无@符号与passwordpassword_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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/108387.html

相关文章

  • 从前后端分别学习——注册/登录流程2

    摘要:昨天研究了网站的注册流程,感兴趣的可以看下从前后端分别学习注册登录流程今天接着研究注册登录流程之登录。为解决这个问题,引入,它是由一组随机数组合的哈希表,当用户登录成功,本来发放给用户,现在变成发放给用户。 昨天研究了网站的注册流程,感兴趣的可以看下:从前后端分别学习——注册/登录流程1 今天接着研究注册/登录流程之登录。 登录 首先来看一下登陆过程:showImg(https://s...

    happyhuangjinjin 评论0 收藏0
  • Vue中的验证登录状态

    摘要:用存储用户路由守卫路由中设置的字段就在当中每次跳转的路径登录状态下访问页面会跳到如果没有访问任何页面。一个简单的保存登录状态的小。 Vue项目中实现用户登录及token验证 先说一下我的实现步骤: 使用easy-mock新建登录接口,模拟用户数据 使用axios请求登录接口,匹配账号和密码 账号密码验证后, 拿到token,将token存储到sessionStorage中,并跳转到首...

    draveness 评论0 收藏0
  • react技术栈实践(从前到后撸一个电影搜集应用)

    摘要:其实,该复杂的东西在哪放都复杂,只不过现在更清晰一点使用不好的地方就是太繁琐了,定义各种各种组件。。。。。 之前做了个好电影搜集的小应用,前端采用react,后端采用express+mongodb,最近又将组件间的状态管理改成了redux,并加入了redux-saga来管理异步操作,记录一些总结 在线地址 手机模式 源码 主要功能 爬取豆瓣电影信息并录入MongoDB 电影列表展示...

    tigerZH 评论0 收藏0
  • 迈出全栈第一步,vue+node+mysql独立完成前后端分离的增删改查流程

    摘要:本使用创建本地服务器,在就能完成全部流程,并不需要线上服务器。路径要与后端接口一致。后端返回成功后,前端数据中对应的元素也要删掉,更新视图。控制器里拿一个方法出来说一下吧,完整的代码都在。读取操作完成后调用释放连接。 写在前面 本文只是本人学习过程的一个记录,并不是什么非常严谨的教程,希望和大家一起共同进步。也希望大家能指出我的问题。适合有一定基础,志在全栈的前端初学者学习,从点击按钮...

    fsmStudy 评论0 收藏0

发表评论

0条评论

Gemini

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<