Node.js+Express+Mysql实现登陆注册

需求:只能登陆的用户访问主页,没有登陆就跳转到登陆页。

一、划分目录

  • control: 分离处理接口的方法
  • dao: 处理数据库的基本操作(无逻辑增删改查)
  • service: 有逻辑的增删改查(例如新增用户:1.先判断用户是否存在 2. 在添加用户)
  • static: 存储静态资源
  • utils: 存储工具文件
  • vaild: 存放需要权限认证的页面, 也就是登陆了才能访问的页面
    server.js: 启动服务主程序的文件
    sqlServer.js: 启动连接sql的服务文件在这里插入图片描述

二、安装模块

npm install express 安装 express 框架
npm install body-parser 安装body-parser 用于解析post请求的参数
npm install cookie-parser 安装cookie-parser 用于解析cookie
npm install mysql 安装mysql 用于连接mysql数据库
npm install mime 安装mime 用于解析 媒体类型 例如: text/html、application/json等

创建数据库表
create database test; 
create table _user(
	u_id bigint primary key auto_increment,
    u_name varchar(40) not null,
    u_pass varchar(80) not null,
    u_eamil varchar(35) not null
)
搭登陆页面
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>LOGIN</title>
  <link rel="stylesheet" href="reset.min.css">
  <!--IMPORT CSS-->
  <style>
    * {
      font-family: "Helvetica Neue", Helvetica, "PingDang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "微软雅黑", Arial;
      font-size: 13px;
    }

    body, html {
      width: 100%;
      height: 100%;
    }

    body {
      background: #eef2f5;
    }

    .login-box {
      position: relative;
      top: 30%;
      left: 50%;
      transform: translateX(-50%);
      box-sizing: border-box;
      width: 300px;
      background: #fff;
    }

    .padding-box {
      padding: 0 25px;
    }

    .check-l-box {
      text-align: center;
      height: 50px;
      line-height: 50px;
    }

    .check-l-box > span {
      cursor: pointer;
    }

    .check-l-box > span.mark {
      border-bottom: 1px solid black;
    }

    /*登陆表单*/
    .message-form {
      color: #7b7b7b;
      display: none;
    }

    .message-form > label {
      line-height: 35px;
      display: block;
    }

    .message-form > label > input {
      display: block;
      width: 100%;
      height: 28px;
      box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
      border: 1px solid #ddd;
    }

    .message-form > label > input:focus {
      outline: 0;
      box-shadow: 0 0 8px lightblue;
    }

    #login-submit, #reg-submit {
      margin-top: 10px;
      margin-bottom: 10px;
      height: 30px;
      width: 100%;
      border: 1px solid #ddd;
    }

    .message-form.check {
      display: block;
    }

    .tips {
      text-align: center;
      color: red;
    }
  </style>
</head>
<body>

<section class="login-box">
  <div class="padding-box">
    <div class="check-l-box">
      <span class="mark">登陆</span>
      <span>注册</span>
    </div>
    <!-- 提示: 后期发送请求返回告诉用户信息 -->
    <p class="tips"></p>
	<!-- 登陆表单 -->
    <form class="message-form check" method="post" action="/user/login">
      <label for="account">
        用户名:<input type="text" id="account" name="account">
      </label>
      <label for="password">
        密码:<input type="password" id="password" name="password">
      </label>
      <input type="submit" value="登陆" id="login-submit">
    </form>
    <!-- 注册表单 -->
    <form class="message-form" method="post" action="/user/reg">
      <label for="reg-account">
        用户名:<input type="text" id="reg-account" name="reg_account">
      </label>
      <label for="reg-password">
        密码:<input type="password" id="reg-password" name="reg_password">
      </label>
      <label for="user-email">
        邮箱:<input type="text" id="user-email" name="user_email"/>
      </label>
      <input type="submit" value="注册" id="reg-submit">
    </form>
  </div>
</section>


<!-- IMPORT JS -->

<script>
   // 登陆/注册按钮切换
  let forms = document.querySelectorAll('.message-form'),
      marks = document.querySelectorAll('.check-l-box>span')


  let clearCheck = () => {
    for (let i = 0; i < forms.length; i++) {
      forms[i].className = 'message-form'
      marks[i].className = ''
    }
  }
  for (let i = 0; i < marks.length; i++) {
    marks[i].addEventListener('click', function () {
      clearCheck();
      this.className = 'mark'
      forms[i].className = 'message-form check'
    }, false)
  }
 // 发送请求部分
 let loginBtn = document.querySelector('#login-submit'),
     regBtn = document.querySelector('#reg-submit'),
     tip = document.querySelector('.tips');

  // 简单封装ajax请求 封装为pomise 
  let sendReq = (method, path, pram) => {
    return new Promise((resolve, reject) => {
      let xhr = new XMLHttpRequest()
      xhr.open(method, path)
      xhr.onreadystatechange = function () {
        if (xhr.status === 200 && xhr.readyState === 4) {
          resolve(xhr.responseText)
        }
      }
      xhr.onerror = () => {
        reject()
      }
      xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8')
      xhr.send(pram)
    })
  }

  loginBtn.onclick = function (ev) {
    let account = document.querySelector('#account'),
        password = document.querySelector('#password'),
        par = 'account' + '=' + account.value + '&' + 'password' + '=' + password.value;
    console.log(par)
    sendReq('POST', '/user/login', par).then(res => {
      // 密码正确跳转,没有正确则显示错误信息
      try {
        res = JSON.parse(res)
        res.code === 0 ? tip.innerText = res.message : location.href = 'http://localhost:8080';
      } catch (e) {
        location.href = 'http://localhost:8080';
      }
    })
    ev.preventDefault()
  }

  regBtn.onclick = function (ev) {
    let account = document.querySelector('#reg-account'),
        password = document.querySelector('#reg-password'),
        email = document.querySelector('#user-email'),
        par = 'account' + '=' + account.value + '&' + 'password' + '=' + password.value + '&email=' + email.value;
    sendReq('POST', '/user/reg', par).then(res => {
      let {code, message} = JSON.parse(res);
      tip.innerText = message
      if (code === 1) {
        clearCheck()
        marks[0].className = 'mark'
        forms[0].className = 'message-form check'
      }
    })
    ev.preventDefault()
  }
</script>
</body>
</html>

写完上面的代码我们就可以得到这么一个页面
在这里插入图片描述

主页搭建

主页代码十分简单,只要用于测试
注: 主页需要放在valid文件夹下

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<h2>我是主页</h2>
</body>
</html>
编写工具方法

在utils目录中: 案例中主要是把 读文件 / sql 操作封装为promise

  • promiseFS.js
/*
* 将fs中的常用 I/O 操作封装为promise版本
* */

let fs = require('fs'),
    path = require('path'),
    resultObj = {};

let suffixHandle = (pathname) => {
  let suffixReg = /\.(PNG|JPG|JPEG|WEBP|ICO|BMP|SVG|MP4|MP3|M3U8|WAV|OGG)$/i
  return suffixReg.test(pathname)
}

// READ-FILE / READ-DIR / MK-DIR / RM-DIR / UN-LINK /
/*
* 读取文件时 需要使用编码 以及过滤 富媒体 文件
* */
['readFile', 'readdir', 'mkdir', 'rmdir', 'unlink'].forEach(item => {
  resultObj[item] = function (pathname, encoding = 'utf8') {
    return new Promise((resolve, reject) => {
      let callback = function (err, res) {
        !err ? resolve(res) : reject(err)
      }
      // 如果是富媒体编码就为null
      suffixHandle() ? encoding = null : null
      pathname = path.resolve(pathname)

      if (item !== 'readFile') {
        encoding = callback
        encoding = null
      }
      fs[item](pathname, encoding, callback)
    })
  }
});

// WRITE-FILE / APPEND-FILE
['writeFile', 'appendFile'].forEach(item => {
  resultObj[item] = function (pathname, content, encoding = 'utf8') {
    // 支持JSON类型数据
    (typeof content === 'object' && content !== null) ? content = JSON.stringify(content) : null
    // 将传入的内容转为 对象
    typeof content !== 'string' ? content += '' : null
    return new Promise((resolve, reject) => {
      let callback = function (err, res) {
        !err ? resolve(res) : reject(err)
      }
      pathname = path.resolve(pathname)
      fs[item](pathname, content, encoding, callback)
    })
  }
})

// COPYFILE
resultObj['copyFile'] = function (pathname1, pathname2) {
  return new Promise((resolve, reject) => {
    // 解析路径 为了防止路径错误
    pathname1 = path.resolve(pathname1)
    pathname2 = path.resolve(pathname2)

    let callback = function (err) {
      !err ? resolve() : reject(err)
    }

    fs['copyFile'](pathname1, pathname2, callback)
  })
}

module.exports = resultObj
  • promiseSQL
/**
 * con: 连接sql的connection对象
 * sql: sql语句
 */
let querySql = (con, sql) => {
  return new Promise((resolve, reject) => {
    con.query(sql, (err, result) => {
      if (err) reject(err)
      resolve(result)
    })
  })
}

module.exports = {
  querySql
}
连接数据库

在dao目录下创建两个文件
在这里插入图片描述

  • baseSql.js: 放着无逻辑sql操作
let promiseSQL = require('../utils/promiseSQL');


/**
 * @param con: sql连接对象
 * @param table: 需要查询的表
 * @param condition: 需要查询的条件 Object
 * */
let query = (con, table, condition) => {
  let cKeys = Object.keys(condition),
      pram1, pram2 = '';
  if (cKeys.length !== 0) {
    pram1 = `where ${cKeys[0]}='${condition[cKeys[0]]}'`;
    if (cKeys.length === 2) {
      pram2 = `and ${cKeys[1]}=${condition[cKeys[1]]}`
    }
  }
  return promiseSQL.querySql(con, `select * from ${table} ${pram1} ${pram2}`)
}

/**
 * @param con: sql连接对象
 * @param table: 需要插入的表
 * @param condition: 需要插入的对象
 * */
let insert = (con, table, condition) => {
  let cKeys = Object.keys(condition)
  if (cKeys.length === 0) return new Promise.reject('插入无效')
  let attr = '', val = '';
  cKeys.forEach(key => {
    attr += key + ','
    val += `'${condition[key]}',`
  })
  attr = attr.substring(0, attr.length - 1)
  val = val.substring(0, val.length - 1)
  let sql = `insert into ${table + '(' + attr + ')'} values ${'(' + val + ')'}`
  return promiseSQL.querySql(con, sql)
}

module.exports = {
  query,
  insert
}

sqlConfig.js: 配置sql文件

module.exports = {
  host:'localhost',
  user:'root',
  password:'123456',
  database:'test'
}
编写服务操作
  • loginService.js用于查询用户和增加用户
/* 关于登陆的查询服务 */
let baseSql = require('../dao/baseSql')
// 查询用户
let queryUser = (con, uname, password) => {
  let queryObj = {u_name: uname}
  if (password) {
    queryObj.u_pass = password
  }
  return baseSql.query(con, '_user', queryObj)
}

// 插入用户
let insertUser = (con, uname, upassword, email) => {
  let insertObj = {u_name: uname, u_pass: upassword, u_email: email}
  return baseSql.insert(con, '_user', insertObj)
}

module.exports = {
  queryUser,
  insertUser
}
编写controller(登陆/注册逻辑)

control文件夹下新建loginController.js

  • loginController.js
let loginService = require('../service/loginService'),
    connection = require('../sqlServer')
/* 登陆逻辑 */
let loginHandle = (req, res) => {
  res.clearCookie('userState')
  let {account, password} = req.body
  res.status(200)
  res.type('application/json')
  // 传入空字符
  if (!account || !password) {
    res.send({
      code: 0,
      message: '请输入账号或密码!'
    })
  }
  // 查询数据库 验证用户是否正确
  loginService.queryUser(connection, account, password).then(result => {
    let check = result.length === 0
    // 用户不存在
    if (check) {
      res.send({code: 0, message: '用户名或密码错误'})
    } else {
      // 保存登陆信息到cookie
      res.cookie('userState', {isLogin: true, username: account})
      res.redirect('/')
    }
  })
}

/* 注册逻辑 */
let regHandle = (req, res) => {
  let {account, password, email} = req.body;
  // 查询用户是否存在
  loginService.queryUser(connection, account).then(result => {
    let check = result.length !== 0;
    // 用户名存在
    if (check) {
      return Promise.reject()
    }
    return Promise.resolve()
  }).then(result => {
    // 不存在就添加用户
    return loginService.insertUser(connection, account, password, email)
  }).catch(() => {
    res.send({code: 0, message: '用户已经存在'})
    return Promise.reject()
  }).then(result => {
    // 添加用户成功
    res.send({code: 1, message: '创建账号成功'})
  }).catch(() => {
  })
}

module.exports = {
  regHandle,
  loginHandle
}
编写服务(sql连接/服务器)
  • sqlServer.js:连接数据库
const mysql = require('mysql'),
    sqlConfig = require('./dao/sqlConfig');

// 创建连接
let connection = mysql.createConnection(sqlConfig)
// 连接数据库
connection.connect((err) => {
  if (err) throw err
  console.log('连接成功')
})
// 导出连接对象
module.exports = connection
  • server.js: 主程序,启动服务器
const express = require('express'),
    bodyParser = require('body-parser'),
    promiseFS = require('../utils/promiseFS'),
    cookieParser = require('cookie-parser'),
    {regHandle, loginHandle} = require('./control/loginController')


// 创建服务
let app = express()
app.listen(8080, () => console.log('SUCCESS!'))

app.use(bodyParser.urlencoded({
  extended: true
}))
app.use(bodyParser.json({
  extended: true
}))
app.use(cookieParser())
app.use(express.static('./static'))


// 处理路径
app.use((req, res, next) => {
  if (req.method === 'GET') {
    let {url} = req,
        cookies = req.cookies;

    url === '/' ? url = '/index.html' : null;
    // 已经登陆
    if (!!cookies.userState) {
      res.type('text/html')
      res.status(200)
      promiseFS.readFile(`./valid${url}`).then(result => {
        res.send(result)
      }).catch(err => {
        res.redirect('/index.html')
      })
      return
    }
    if (url === '/index.html' && !cookies.userState) {
      res.redirect('/login.html')
    }
  }
  next()
})


// 登陆接口
app.post('/user/login', loginHandle)

// 注册接口
app.post('/user/reg', regHandle)
测试

执行node server: 执行server文件启动服务

登陆效果测试
在这里插入图片描述
注册效果测试
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_45412353/article/details/106629736