[BE201] Express & Sequelize part 4


Posted by yymarlerr on 2021-08-24

統整:實作會員註冊系統

前置作業

安裝 bcrypt 幫密碼作 hash

npm install bcrypt

實作

index.js

const express = require('express')
const bodyParser = require('body-parser')
const session = require('express-session')
const flash = require('connect-flash')
const db = require('./db')
const app = express()
const port = 5001

const todoController = require('./controllers/todo'
)
const userController = require('./controllers/user')

app.set('view engine', 'ejs')

app.use(bodyParser.urlencoded({ extended: false })) 
app.use(bodyParser.json()) 
app.use(flash())

app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: true,
}))

app.use((req, res, next) => {
  res.locals.username = req.session.username
  res.locals.errorMessage = req.flash('errorMessage')
  next()
})

function redirectBack(req, res) { // 自己寫的 Middleware
  res.redirect('back')
}

app.post('/todos', todoController.newTodo)
app.get('/todos', todoController.getAll) 
app.get('/todos/:id', todoController.get)
app.get('/', (req, res) => {
  res.render('user/index')  // 首頁
})

app.get('/login', (req, res) => {
  res.render('login')
})

app.get('/login', userController.login)
app.post('/login', userController.handleLogin, redirectBack)
app.get('/logout', userController.logout)
app.get('/register', userController.register)
app.post('/register', userController.handleRegister, redirectBack)

app.post('/login', (req, res) => {
  if (req.body.password === "abc") {
    req.session.isLogin = true 
    res.redirect('/') 
  } else {
    req.flash('errorMessage', 'Please input the correct password')
    res.redirect('/login')
  }
})

app.get('/logout', (req, res) => {
  req.session.isLogin = false
  res.redirect('/')
})

app.listen(port, () => {
  db.connect()
  console.log(`hello world, listening on port ${port}`)
})

controller

  • user.js
const userModel = require('../models/user')
const bcrypt = require('bcrypt')
const saltRounds = 10; // 可以調整密碼的複雜程度

const userController = {
  get: (req, res) => {

  },

  login: (req, res) => {
    res.render('login')
  },

  handleLogin: (req, res, next) => {
    const {username, password} = req.body
    if (!username || !password) {
      req.flash('errorMessage', '資料填寫不齊全')
      return next()
    }

    userModel.get(username, (err, user) => {
      if (err) {
        req.flash('errorMessage', err.toString())
        return next() // 如果不導回其他頁面,瀏覽器會不知道怎麼處理(會一直轉圈圈)
      }
      if (!user) {
        req.flash('errorMessage', '使用者不存在') // 帳號密碼錯誤代表找不到 user
        return next()
      }
      bcrypt.compare(password, user.password, (err, isSuccessful) => {
        if (err || !isSuccessful) {
          req.flash('errorMessage', err.toString())
          return next()
        }
        req.session.username = username
        res.redirect('/')
    });
    })

  },

  logout: (req, res) => {
    req.session.username = null
    res.redirect('/')
  },

  register: (req, res) => {
    res.render('user/register')
  },

  handleRegister: (req, res, next) => {
    // ES6 解構語法
    const {username, nickname, password} = req.body
    if (!username || !nickname || !password) {
      req.flash('errorMessage', '缺少必要欄位') // 這邊用 return 的話,下面的程式碼就不會執行到,用 if else 會有很多層結構
      return next()
    }

    bcrypt.hash(password, saltRounds, (err, hash) => { // 為非同步
      if (err) {
        req.flash('errorMessage', err.toString())
        return next()
      }

      userModel.add({ // hash 過後再將密碼寫進去
        username,
        nickname,
        password: hash
      }, (err) => {
        if (err) {
          req.flash('errorMessage', err.toString())
          return next()
        }
        req.session.username = username // username 這個 key 如果有值代表登入成功了
        res.redirect('/')
      })
    })
  }

}

module.exports = userController

view

  • register.ejs
<h1>註冊頁面</h1>

<h2><%= errorMessage %></h2>
<form method="POST" action="/register">
  <div>Username: <input type="text" name="username" /></div>
  <div>Nickname: <input type="text" name="nickname" /></div>  
  <div>Password: <input type="password" name="password" /></div>
  <input type="submit" />
</form>
  • login.ejs
<h1>登入頁面</h1>

<h2><%= errorMessage %></h2>
<form method="POST" action="/login">
  <div>Username: <input type="text" name="username" /></div>
  <div>Password: <input type="password" name="password" /></div>
  <input type="submit" />
</form>
  • index.js (首頁)
<h1>簡易會員系統</h1>

<!--先判斷是否有登入-->
<% if (username) { %>
  <a href='/logout'>登出</a>
<% } else { %>
  <a href='/register'>註冊</a>
  <a href='/login'>登入</a>
<% } %>
  • Template Engine 已經做好 escape,如果 <%- %> 用減號代表不做 escape

models

  • user.js
const db = require('../db')

const userModel = {
  add: (user, cb) => { // 傳進 user 這個 Object
    db.query(
      'INSERT INTO yide_users (username, password, nickname) VALUES (?, ?, ?)', 
      [user.username, user.password, user.nickname],
      (error, results) => { 
        if (error) return cb(error);
        cb(null)
    });
  },

  get: (username, cb) => {
    db.query(
      'SELECT * FROM yide_users WHERE username = ?', [username], 
      (error, results) => { 
        if (error) return cb(error);
        cb(null, results[0])
    });
  }
}

module.exports = userModel









Related Posts

[12] 值 - 陣列、類陣列

[12] 值 - 陣列、類陣列

[JS] undefined 和 null 的差異

[JS] undefined 和 null 的差異

[ Week4 ] - 網路基礎概論:關於 HTTP

[ Week4 ] - 網路基礎概論:關於 HTTP


Comments