如何使用Node.js核心快速搭建伺服器 【完整教程原始碼】

Jeery_譚金傑發表於2019-03-15
'假設你已經瞭解什麼是express框架 mongodb資料庫(非關係型資料庫) 那麼讓我們開始吧!'


Node.js的核心,通過npm下載或者自己有的包,require引入時不需要加/,
但是自己寫的模組則需要加,否則找不到,Node.js使用的是commonJs的模組
化規範哦 
 
'讓我們開始吧!' 
複製程式碼

github原始碼地址

專案需求

  • 登陸,註冊,主頁,各一個介面(路由)
  • 不需要靜態資源,需要前後端能互動,在對應的位置提醒使用者更新不同的內容
  • 使用者如果不登陸,是不能訪問主頁。如果使用者在規定有效期內登陸成功過,也可以訪問主頁
  • 使用者如果登陸/註冊失敗,要提醒對應的全部失敗原因,並且除了密碼外都要儲存在頁面中(不讓客戶重複輸入)
  • 使用者的密碼在儲存資料庫中時,需要使用sha1的包進行加密,在查詢時候也需要加密
  • 使用者的session_id也需要加密然後儲存在資料庫中
  • 對使用者的session_id所對應的cookie也要設定一個有效期
  • ...後續可能加一些防攻擊的需求,在應用型的中介軟體中。

所需要的下載的包
  "dependencies": {
    "cookie-parser": "^1.4.4",    //解析cookie 
    "ejs": "^2.6.1",   //ejs的模板渲染
    "express": "^4.16.4",    //express框架 
    "express-session": "^1.15.6",   //session的處理  
    "mongoose": "^5.4.19"   //mongoose包,處理mongodb資料庫的
  },
  "devDependencies": { 
    "nodemon": "^1.18.10",  //每次修改JS檔案後伺服器自動更新
    "sha1": "^1.1.1"   //對資料加密的模組 
  }
  
  
'直接複製我上面的json檔案內容,把後面的註釋刪除掉即可 npm install  下載'


 我們使用ejs的渲染引擎進行渲染
 
 首先看主檔案的入口  
 
 -------
 ejs.js  
 '這裡首先引入了express框架後,還引入了session的處理模組,以及自定義的uirouter、
 inforrouter兩個路由器路由模組 ,設定了ejs的渲染目錄views,還指定了渲染
 引擎為ejs(ejs的模組不需要引入,設定就好),'
const express = require('express');
const app = express();
const mongoose = require('./database');
const uirouter = require('./uirouter');
const inforouter = require('./inforouter');
const session = require('express-session');
const MongoStore = require('connect-mongo')(session);
app.set('views', 'views');
app.set('view engine', 'ejs');
app.use(session({     //設定session
    secret: 'hello',     //對session_id的加密 
    saveUninitialized: false,   
    resave: false,  
    store: new MongoStore({
        url: 'mongodb://localhost:27017/user',
        touchAfter: 24 * 3600   
    }),
    cookie:{maxAge:1000*3600*24*7},   
    //session是基於cookie傳送,cookie預設是會話儲存,需要指定有效期
})) 
mongoose.then(async () => {      //設定路由器路由,從下往下解析 
    app.use(uirouter);    
    app.use(inforouter);
})
app.use((err, req, res, next) => {  //錯誤機制處理路由 
    if (err) {
        console.log(err);
    }
})
app.listen(5555, err => {   //監聽埠號 
    if (!err) {
        console.log('監聽成功');
    } else {
        console.log(`監聽失敗${err}`);
    }
});
 
 ----------------
 
 'database.js 這個是資料庫的連線模組 '
 
 ---------------------
 
 '這裡暴露的是一個promise物件,必須先連線成功資料庫再走下一步,所以主模組使用了.then()'
const mongoose = require('mongoose');
module.exports = new Promise((resolve, reject) => {
    mongoose.connect('mongodb://localhost:27017/user', { useNewUrlParser: true, useCreateIndex: true });
    //指定連線的域名和埠號,以及資料庫的名稱
    
    mongoose.connection.once('open', err => {
        if (!err) {
            console.log('資料庫連線成功');
            resolve();
        } else {
            console.log('資料庫連線失敗:' + err);
            reject(err);
        }
    })
})


---------------------
 
 
 'model.js 這個是模型物件模組, 暴露的是模型物件,可以使用它身上對資料庫的CRUD操作'
 
const { Schema, model } = require('mongoose');
const schema = new Schema({
    username: {
        type: String,
        unique: true,
        required: true
    },
    password: {
        type: String,
        required: true
    },
    email: {
        type: String,
        required: true
    }
});
const Model = model('userinfos', schema);  
//指定資料庫的模型物件,以及儲存的集合名稱為 'userinfos'
module.exports = Model;

-----


'inforrouter 這個是註冊模組的檔案 '

const { Router, urlencoded } = require('express');
const sha1 = require('sha1');  
const router = new Router();
const model = require('./model');
router.use(urlencoded({ extended: true }));
router.post('/login', async (req, res) => {
    let { username, password } = req.body;
    const result = await model.findOne({ username, password: sha1(password) });
    //'如果能通過使用者名稱和加密後的密碼在資料庫中查詢到,那麼給它新增session_id然後跳轉到主頁'
    if (result) {
        req.session.userid = result.id
        res.redirect('/index')
    } else {
        res.render('login', { err: { username: username, usernameerr: '使用者名稱或密碼錯' } });
        //'如果找不到,渲染login.ejs檔案,並且渲染對應的err資料到頁面上'
    }
})
router.post('/re', async (req, res) => {
    let { username, password, repassword, email } = req.body;
    //ES6的解構賦值,提取post請求體中的四個對應的數值
    const usernameReg = /^[A-Za-z0-9_]{5,10}$/;  
    const passwordReg = /^[A-Za-z0-9_]{5,12}$/;
    const emailReg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
    //三個正則匹配
    let err = {};
    const result = await model.findOne({ username });
    if (!usernameReg.test(username)) {
        err.usernameerr = '使用者名稱格式不正確';
    }
    if (!passwordReg.test(password)) {
        err.password = '密碼格式不正確';
    }
    if (password != repassword) {
        err.repassword = '跟上次輸入密碼不一致';
    }
    if (!emailReg.test(email)) {
        err.email = '郵箱格式不正確';
    }
    if (result) {
        err.usernameerr = '使用者名稱已存在'
    }
    
    if (err.usernameerr || err.password || err.repassword || err.emailerr) {
        res.render('re', { err })
        return;
        //'只要有一個錯誤存在,那麼就渲染re.ejs檔案,並且渲染err這個資料'
    }
    model.create({
        username,
        password: sha1(password),
        email
        
    })
    res.redirect('/index')
    //'如果上面的錯誤都沒有,那麼就儲存這個文件物件在資料庫中,
    並且跳轉到index的路由上,index的路由也有處理機制'
})
module.exports = router;

------------


'uirouter.js 是處理get請求的路由檔案'


----------------


const { Router } = require('express');
const model = require('./model');
const router = new Router();
router.get('/login', async (req, res) => {
    res.render('login.ejs', { err: '' })
    //'渲染對應的ejs檔案,這裡如果不寫空的err渲染資料,
    後期的渲染會報錯,會提示err not defined'
})
router.get('/re', (req, res) => {
    res.render('re.ejs', { err: "" });
})
router.get('/index', async (req, res) => {
    const { userid } = req.session;
    if (!userid) {
        res.redirect('/login')
        return;
    }
    const result = await model.findById({ _id: userid });
    if (result) {
        res.render('index');
    } else {
        res.redirect('/login');
    }
    //'如果使用者成功登陸過,那麼會有一個session_id檔案,
    如果有並且能在資料庫中找到,那麼就讓使用者去index.ejs
    的渲染檔案'
})
module.exports = router;



-----------------


'views資料夾,下面的三個ejs檔案,是使用ejs渲染引擎的渲染檔案'


'index.ejs '


<!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>
</head>

<body>
    <h1>歡迎來到首頁</h1>
    <a href="/login">登陸介面</a>
    <a href="/re">註冊介面</a>
</body>

</html>


-------------------------

'login.ejs 是登陸檔案'



<!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>
    <style>
        .err{
            color:red;
        }
    </style>
</head>

<body>
    <form action="/login" method="post">
        <label for="username">使用者名稱</label>
        <input type="text" name="username" value=<%= err.username %> ><span class="err"> <%= err.usernameerr %> </span> <br />
        <label for="password">密碼</label>
        <input type="password" name="password"><span  class="err"> </span> <br />
        <input type="submit" value='登陸'>
    </form>
</body>

</html>


-------------------



're.js 是註冊頁面檔案  '


<!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>
    <style>
        .err{
            color:red;
        }
    </style>
</head>
<body>
    <form action="/re" method="post">
        <label for="username">使用者名稱</label>
        <input type="text" name="username"  value=<%= err.username %>  ><span class='err'><%= err.usernameerr %></span><br />
        <label for="password">密碼</label>
        <input type="password" name="password"><span class='err'><%= err.password%></span><br />
        <label for="repassword">再次確認密碼</label>
        <input type="password" name="repassword"><span class='err'><%= err.repassword%></span><br />
        <label for="email">郵箱</label>
        <input type="text" name="email"   value=<%= err.email %> ><span class='err'><%= err.emailerr%></span><br />
        <input type="submit" value='註冊'>
    </form>
</body>
</html>

--------------------------

'在瀏覽器中輸入的url地址訪問都是get請求,所以我們把get請求也成為uirouter,但是一旦到了提交表單,
我們使用的就是post請求的路由了,同樣名字的路由,但是請求方式不一樣他們並不衝突,Node.js中程式碼
也是從上到下解析的,這裡的ejs渲染模板需要多注意的是get請求的時候一定要先給一個空的渲染資料,否
則後期post請求是渲染不上去的,會提示not defined,因為肯定是先get請求看到頁面後才能提交資料,這
點要格外注意,其實express框架並不難,這個版本就能用到express中最核心的所有知識點,能掌握了就能
獨立開發伺服器和前後端互動以及資料處理',後面我會就web的儲存技術,以及koa2框架繼續深入


複製程式碼

相關文章