Node.Js_Express_Part2.配置註冊登入介面與功能實作與MySQL資料庫配置


Step1.新增配置頁面路由與連結
新增兩個ejs檔案
views\user\login.ejs
views\user\register.ejs


app.js程式調整新增路由

const express = require('express')
//const path = require('path');
const app = express()

app.set('view engine','ejs') //設置默認採用模板引擎名稱
app.set('views','./views') //設置模板頁面存放路徑

//將node_modules資料夾,託管為靜態資源目錄
app.use('/node_modules',express.static('./node_modules'))
//app.use('/node_modules', express.static(path.join(__dirname, 'node_modules')));

app.get('/', (req,res) => {
    //使用render函數之前,必須確保已經安裝和配置好ejs模板引擎
    res.render('index.ejs',{name:'王曉明' , age:25})
})


app.get('/register' , (req,res) => {
    res.render('./user/register.ejs',{})
})

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

app.listen(80, () => {
    console.log('server running at http://127.0.0.1')
})


./views/index.ejs  
更改為a標籤設置指向跳轉的程式請求網址

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap-theme.min.css">
    <script src="/node_modules/jquery/dist/jquery.min.js"></script>
    <!-- 叮嚀:bootstrap的js是有依賴jquery文件的,要在之前先配置jquery。 -->
    <script src="/node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
</head>
<body>

    <!-- 導覽列區塊 -->
    <nav class="navbar navbar-default">
        <div class="container-fluid">
          <!-- Brand and toggle get grouped for better mobile display -->
          <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
              <span class="sr-only">Toggle navigation</span>
              <span class="icon-bar"></span>
              <span class="icon-bar"></span>
              <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="/">論壇</a>
          </div>
      
          <!-- Collect the nav links, forms, and other content for toggling -->
          <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">

            <div class="nav navbar-nav navbar-right navbar-form">
              <a class="btn btn-success" href="/register">註冊</a>
              <a class="btn btn-primary" href="/login">登入</a>
            </div>

            <div class="nav navbar-nav navbar-right navbar-form">
              <button class="btn btn-warning">歡迎</button>
              <button class="btn btn-danger">註銷</button>
            </div>

            <ul class="nav navbar-nav navbar-right">
              <li class="dropdown">
                <a href="#" class="dropdown-toggle"
                  data-toggle="dropdown" role="button"
                  aria-haspopup="true" 
                  aria-expanded="false">發表
                  <span class="caret"></span>
                </a>
                <ul class="dropdown-menu">
                  <li>
                    <a href="#">文章</a>
                  </li>
                  <li>
                    <a href="#">問題</a>
                  </li>
                </ul>
              </li>
            </ul>
          </div><!-- /.navbar-collapse -->
        </div><!-- /.container-fluid -->
    </nav>

    <h1>貼文列表</h1>

    <!-- 版權聲明區塊 -->
      <div class="text-center text-muted">
        AAA © BBB 2025
      </div>

</body>
</html>


Step2.註冊畫面的內容

views\user\register.ejs

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap-theme.min.css">
    <style>
        #form {
            width: 400px;
            margin: 0 auto;
            margin-top: 100px;
        }

        h1 {
            text-align: center;
        }

        input[type='submit']{
            width: 100%;
        }
    </style>
</head>
<body>
    <form id="form">
        <h1>註冊畫面</h1>
        <div class="form-group">
            <input type="text" name="username" id="username" class="form-control input-lg" placeholder="帳號名" required>
        </div>
        <div class="form-group">
            <input type="password" name="password" id="password" class="form-control input-lg" placeholder="密碼" required>
        </div>
        <div class="form-group">
            <input type="text" name="nickname" id="nickname" class="form-control input-lg" placeholder="暱稱" required>
        </div>
        <div class="form-group">
            <input type="submit" value="註冊新用戶" class="btn btn-primary btn-lg">
        </div>
    </form>
    <script src="/node_modules/jquery/dist/jquery.min.js"></script>
    <script>
        $(function(){
            $('#form').on('submit' , function(e){
                e.preventDefault()
                $.ajax({
                    url: '/register',
                    data: $('#form').serialize(),
                    type: 'POST',
                    dataType: 'json',
                    success: function (result){
                        console.log(result)
                    }
                })
            })
        })
    </script>
</body>
</html>



Step3.登入畫面的內容


views\user\login.ejs

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap-theme.min.css">
    <style>
        #form {
            width: 400px;
            margin: 0 auto;
            margin-top: 100px;
        }

        h1 {
            text-align: center;
        }

        input[type='submit']{
            width: 100%;
        }
    </style>
</head>
<body>
    <form id="form">
        <h1>登入畫面</h1>
        <div class="form-group">
            <input type="text" name="username" id="username" class="form-control input-lg" placeholder="帳號名" required value="王某某">
        </div>
        <div class="form-group">
            <input type="password" name="password" id="password" class="form-control input-lg" placeholder="密碼" required value="1234">
        </div>
        <div class="form-group">
            <a href="/register" class="pull-right">去註冊</a>
        </div>
        <div class="form-group">
            <input type="submit" value="登入" class="btn btn-primary btn-lg">
        </div>
    </form>
    <script src="/node_modules/jquery/dist/jquery.min.js"></script>
    <script>
        $(function(){
            $('#form').on('submit' , function(e){
                e.preventDefault()
                $.ajax({
                    url: '/login',
                    data: $('#form').serialize(),
                    type: 'POST',
                    dataType: 'json',
                    success: function (result){
                        console.log(result)
                    }
                })
            })
        })
    </script>
</body>
</html>



Step4.下載表單提交用於解析請求資料的套件

npm i body-parser -S

body-parser 是Express 常搭配的中介軟體,用來解析body請求資料。
比方POST 一筆JSON 格式的資料上傳至Express後端應用,就可透過body-parser 解析這筆資料。



Step5.配置bodyParser並測試套件功能

./app.js

const express = require('express')
//const path = require('path');
const app = express()
const bodyParser = require('body-parser')

app.set('view engine','ejs') //設置默認採用模板引擎名稱
app.set('views','./views') //設置模板頁面存放路徑

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

//將node_modules資料夾,託管為靜態資源目錄
app.use('/node_modules',express.static('./node_modules'))
//app.use('/node_modules', express.static(path.join(__dirname, 'node_modules')));

app.get('/', (req,res) => {
    //使用render函數之前,必須確保已經安裝和配置好ejs模板引擎
    res.render('index.ejs',{name:'王曉明' , age:25})
})

app.get('/register' , (req,res) => {
    res.render('./user/register.ejs',{})
})

app.post('/register' , (req,res) => {
    const body = req.body
    console.log(body)
    res.send({msg:'ok' , status:200})
})

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

app.listen(80, () => {
    console.log('server running at http://127.0.0.1')
})








Step6.註冊流程梳理與配置MySQL套件與創建資料庫與表
可嘗試用draw.io來進行流程梳理,確認預期實作的功能有哪些。

下載mysql套件
npm i mysql -S

創建MySQL資料庫與表










phpmyadmin 設置資料庫帳號密碼
預設的帳號是 root ,沒有密碼
http://localhost:8090/phpmyadmin


會看到一個使用者名稱叫「任何」,且主機名稱為「%」的帳號
代表讓所有人都不用輸入密碼即可進入到資料庫管理後台。



先將其勾選並底下也勾起【刪除與使用者同名的資料庫。】再按執行


按確定



針對三個root的帳號,其主機名稱分別如下
127.0.0.1
::1
localhost
三個都要設定相同密碼









當返回使用者帳號頁面,即可發覺密碼欄位更新為「是」

當三個都調整完基本上會跳出此報錯權限畫面


此時就要去改phpMyAdmin的config檔
可從XAMPP裡按Apache的config,當中有一個phpMyAdmin(config.inc.php)


將第19行的config更改為cookie,就代表強制跳出躍登入的網頁畫面。
再往下分別是
第20行表示你預設phpmyadmin資料庫登入帳號為root
第21行表示剛剛設定的三組相同密碼
可自行維護上去


當設定後再重整phpmyadmin網頁後台,即可跳出登入畫面。
再次輸入剛剛設置的帳密即可
Step7.應用端實作表單註冊邏輯,比照流程圖設計。

下載時間戳格式化套件moment(),把時間格式化成標準格式或其他格式
https://momentjs.com/

npm install moment --save


app.js程式碼(增加註冊流程)

const express = require('express')
//const path = require('path');
const app = express()
const bodyParser = require('body-parser')
const mysql = require('mysql')
const moment = require('moment') //獲取當前時間戳
const conn = mysql.createConnection({
    port:3306,
    host: '127.0.0.1',
    database: 'blog_db',
    user: 'root',
    password: 'root'
})


app.set('view engine','ejs') //設置默認採用模板引擎名稱
app.set('views','./views') //設置模板頁面存放路徑

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

//將node_modules資料夾,託管為靜態資源目錄
app.use('/node_modules',express.static('./node_modules'))
//app.use('/node_modules', express.static(path.join(__dirname, 'node_modules')));

app.get('/', (req,res) => {
    //使用render函數之前,必須確保已經安裝和配置好ejs模板引擎
    res.render('index.ejs',{name:'王曉明' , age:25})
})

app.get('/register' , (req,res) => {
    res.render('./user/register.ejs',{})
})

app.post('/register' , (req,res) => {
    const body = req.body
    console.log(body)
    if(body.username.trim().length <= 0 || 
        body.password.trim().length <=0 || 
        body.nickname.trim().length <=0){
            return res.send({msg: '請填寫完整表單欄位,再註冊帳號!',status:501})
        }
    //確認是否帳號名有重複
    const sql_str = 'select count(*) as count from blog_users where username=?'
    conn.query(sql_str,body.username,(err,result) => {
        console.log(err)
        console.log('result1:')
        console.log(result)
        if(err) 
            return res.send({msg:'帳號名查重異常!',status:502})
        if(result[0].count !==0) 
            return res.send({msg:'請改用其他帳號名重新註冊!',status:503})
        
        body.ctime = moment().format('YYYY-MM-DD HH:mm:ss')
        const sql_str2 = 'insert into blog_users set ?'
        console.log(body)
        conn.query(sql_str2 , body , (err,result) => {
            console.log('result2:')
            console.log(result)
            if(err || result.affectedRows !== 1) {
                console.log(err)
                return res.send({msg:'註冊新帳號失敗!',status:504})
            }
                
            res.send({msg:'註冊新帳號成功' , status:200})
        })
    })
})

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

app.listen(80, () => {
    console.log('server running at http://127.0.0.1')
})


在預設配置的資料庫連接對象指向要吻合phpmyadmin的資料庫名稱

這裡的 insert into blog_users set ? 是 MySQL 的一個語法簡化。
它的作用是將 body 中的每個屬性當作欄位與對應的值插入到 blog_users 表中。
以下是其具體運作機制:
  • set ? 的語法:MySQL 可以將一個物件直接傳給 ?,以替代傳統 insert into table (col1, col2, ...) values (?, ?, ...) 的寫法。
  • 自動映射屬性:body 物件的屬性名稱會自動映射到資料庫的欄位名稱,例如,如果 body 物件中有 username、password 和 nickname 屬性,它們會自動對應到 blog_users 表中相應的欄位。
  • 安全性:這種寫法也幫助防止 SQL 注入攻擊,因為 conn.query 會使用預處理語句(parameterized queries)將值進行轉義。
  • SQL 簡化:insert into blog_users set ? 的語法比傳統插入語句更加簡潔,不必顯示列出每個欄位,適合在插入許多欄位的情況下快速編寫。

views\user\register.ejs
畫面部分的html程式碼。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap-theme.min.css">
    <style>
        #form {
            width: 400px;
            margin: 0 auto;
            margin-top: 100px;
        }

        h1 {
            text-align: center;
        }

        input[type='submit']{
            width: 100%;
        }
    </style>
</head>
<body>
    <form id="form">
        <h1>註冊畫面</h1>
        <div class="form-group">
            <input type="text" name="username" id="username" class="form-control input-lg" placeholder="帳號名" required>
        </div>
        <div class="form-group">
            <input type="password" name="password" id="password" class="form-control input-lg" placeholder="密碼" required>
        </div>
        <div class="form-group">
            <input type="text" name="nickname" id="nickname" class="form-control input-lg" placeholder="暱稱" required>
        </div>
        <div class="form-group">
            <input type="submit" value="註冊新用戶" class="btn btn-primary btn-lg">
        </div>
    </form>
    <script src="/node_modules/jquery/dist/jquery.min.js"></script>
    <script>
        $(function(){
            $('#form').on('submit' , function(e){
                e.preventDefault()
                $.ajax({
                    url: '/register',
                    data: $('#form').serialize(),
                    type: 'POST',
                    dataType: 'json',
                    success: function (result){
                        console.log(result)
                        if(result.status !== 200){
                            return alert(result.msg)
                        }
                        location.href = '/login'
                    }
                })
            })
        })
    </script>
</body>
</html>


起初示範成功註冊後,資料庫有成功增加一筆帳號資料。







接續是故意打空白,模擬卡控機制。

接續是已註冊過帳號卡控




Step8.登入流程梳理


Step9.應用端實作表單登入邏輯,比照流程圖設計。

app.js程式碼(增加登入流程)

const express = require('express')
//const path = require('path');
const app = express()
const bodyParser = require('body-parser')
const mysql = require('mysql')
const moment = require('moment') //獲取當前時間戳
const conn = mysql.createConnection({
    port:3306,
    host: '127.0.0.1',
    database: 'blog_db',
    user: 'root',
    password: 'root'
})


app.set('view engine','ejs') //設置默認採用模板引擎名稱
app.set('views','./views') //設置模板頁面存放路徑

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

//將node_modules資料夾,託管為靜態資源目錄
app.use('/node_modules',express.static('./node_modules'))
//app.use('/node_modules', express.static(path.join(__dirname, 'node_modules')));

app.get('/', (req,res) => {
    //使用render函數之前,必須確保已經安裝和配置好ejs模板引擎
    res.render('index.ejs',{name:'王曉明' , age:25})
})

app.get('/register' , (req,res) => {
    res.render('./user/register.ejs',{})
})

app.post('/register' , (req,res) => {
    const body = req.body
    console.log(body)
    if(body.username.trim().length <= 0 || 
        body.password.trim().length <=0 || 
        body.nickname.trim().length <=0){
            return res.send({msg: '請填寫完整表單欄位,再註冊帳號!',status:501})
        }
    //確認是否帳號名有重複
    const sql_str = 'select count(*) as count from blog_users where username=?'
    conn.query(sql_str,body.username,(err,result) => {
        console.log(err)
        console.log('result1:')
        console.log(result)
        if(err) 
            return res.send({msg:'帳號名查重異常!',status:502})
        if(result[0].count !==0) 
            return res.send({msg:'請改用其他帳號名重新註冊!',status:503})
        
        body.ctime = moment().format('YYYY-MM-DD HH:mm:ss')
        const sql_str2 = 'insert into blog_users set ?'
        console.log(body)
        conn.query(sql_str2 , body , (err,result) => {
            console.log('result2:')
            console.log(result)
            if(err || result.affectedRows !== 1) {
                console.log(err)
                return res.send({msg:'註冊新帳號失敗!',status:504})
            }
                
            res.send({msg:'註冊新帳號成功' , status:200})
        })
    })
})

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

app.post('/login' , (req,res) => {
    const body = req.body
    const sql_str = 'select * from blog_users where username = ? and password=?'
    conn.query(sql_str , [body.username,body.password] , (err,result) => {
        if(err || result.length !== 1)
            return res.send({msg:'帳號登入失敗' , status:501})
        res.send({msg:'ok',status:200})
    })
})


app.listen(80, () => {
    console.log('server running at http://127.0.0.1')
})


views\user\login.ejs

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap-theme.min.css">
    <style>
        #form {
            width: 400px;
            margin: 0 auto;
            margin-top: 100px;
        }

        h1 {
            text-align: center;
        }

        input[type='submit']{
            width: 100%;
        }
    </style>
</head>
<body>
    <form id="form">
        <h1>登入畫面</h1>
        <div class="form-group">
            <input type="text" name="username" id="username" class="form-control input-lg" placeholder="帳號名" required value="王某某">
        </div>
        <div class="form-group">
            <input type="password" name="password" id="password" class="form-control input-lg" placeholder="密碼" required value="1234">
        </div>
        <div class="form-group">
            <a href="/register" class="pull-right">去註冊</a>
        </div>
        <div class="form-group">
            <input type="submit" value="登入" class="btn btn-primary btn-lg">
        </div>
    </form>
    <script src="/node_modules/jquery/dist/jquery.min.js"></script>
    <script>
        $(function(){
            $('#form').on('submit' , function(e){
                e.preventDefault()
                $.ajax({
                    url: '/login',
                    data: $('#form').serialize(),
                    type: 'POST',
                    dataType: 'json',
                    success: function (result){
                        console.log(result)
                        if(result.status !== 200){
                            return alert(result.msg)
                        }
                        location.href = '/'
                    }
                })
            })
        })
    </script>
</body>
</html>


測試效果













留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

(2021年度)駕訓學科筆試準備題庫歸納分析_法規是非題