Node.js自學筆記 (6/12)

Router處理

  1. 使用變數代稱設定路由
  2. 使用regular expression設定路由
  3. 路由模組化

使用變數代稱設定路由

  • : 冒號之後為代稱名
  • ? 為選擇性的
  • * 為 wildcard (萬用字元)
// :action 可視為變數,路由變數代稱會進入到`params`
app.get('/my-params1/:action/:id', (req, res) => {
    res.json(req.params);
});
// :action進入到req.params.action
// :id進入到req.params.id
// 若僅給:action,沒有給:id,就會出現404錯誤

app.get('/my-params2/:action?/:id?', (req, res) => {
    res.json(req.params);
});
// ?表這個變數代稱可有可無

app.get('/my-params3/*/*?', (req, res) => {
    res.json(req.params);
});
// *表進入到這個變數代稱會變成array

// 一般來說,?比*常用

小整理,express中,req取得參數的三種方法

  1. req.query
    query是querystring

    说明req.query不一定是get

    // GET /search?q=tobi+ferret
    req.query.q
    // => "tobi ferret"
    
    // GET /shoes?order=desc&shoe[color]=blue&shoe[type]=converse
    req.query.order
    // => "desc"
    
    req.query.shoe.color
    // => "blue"
    
    req.query.shoe.type
    // => "converse"
    

    因为有变态的写法

    // POST /search?q=tobi+ferret
    {a:1,b:2}
    req.query.q
    // => "tobi ferret"
    

    post里看不的,用req.body取。

  2. req.body
    Contains key-value pairs of data submitted in the request body. By default, it is undefined, and is populated when you use body-parsing middleware such as body-parser and multer.

    This example shows how to use body-parsing middleware to populate req.body.

    var app = require('express')();
    var bodyParser = require('body-parser');
    var multer = require('multer'); 
    
    app.use(bodyParser.json()); // for parsing application/json
    app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
    app.use(multer()); // for parsing multipart/form-data
    
    app.post('/', function (req, res) {
      console.log(req.body);
      res.json(req.body);
    })
    

    可以確定的是req.body一定是post請求,express里依赖的中间件必须有bodyParser,不然req.body是没有的。

  3. req.params,針對路由,取得路由變數代稱資訊

使用regrular expression設定路由

app.get(/^\/hi\/?/, (req, res) => {
    let result = {
        url: req.url
    };
    result.split = req.url.split('/');
    res.json(result);
});

請測試:
測試以下的 url:
http://localhost:3000/hi
http://localhost:3000/hi/
http://localhost:3000/hi/123
http://localhost:3000/hi123

// 手機號碼
app.get(/^\/09\d{2}\-?\d{3}\-?\d{3}$/, (req, res) => {
    let u = req.url.slice(1);
    u = u.split('?')[0];
    u = u.split('-').join('');
    res.send(u);
});

建議可以在 RegEx101練習,如,檢測發票號碼:
image

測試,在index.js加上以下程式:

app.get(/^\/my-cellphone\/09\d{2}-?\d{3}-?\d{3}$/, (req, res) => {
    res.send('my-cellphone: '+req.url);
});

實測結果:

若少打一個字,就會找找不到頁面:

-則有或沒有都可以:

index.js修改成這樣,就可以將手機號碼變成特定格式,以方便將來儲存到資料庫中:

app.get(/^\/my-cellphone\/09\d{2}-?\d{3}-?\d{3}$/, (req, res) => {
    let url = req.url.split('?')[0];
    url = url.split('-').join('')
    url = url.slice(13)
    res.send('my-cellphone: ' + url);
});

關於email的正規表達式

可以參考這邊:http://emailregex.com/

index.js的另一種寫法

app.get('/my-params1/:action/:id', (req, res) => {
    res.json(req.params);
});
app.get(/^\/my-cellphone\/09\d{2}-?\d{3}-?\d{3}$/, (req, res) => {
    let url = req.url.split('?')[0];
    url = url.split('-').join('')
    url = url.slice(13)
    res.send('my-cellphone: ' + url);
});

也可以這樣寫:

const func2 = (req, res) => {
    res.json(req.params);
};
app.get('/my-params1/:action/:id', func2);
app.get(/^\/my-cellphone\/09\d{2}-?\d{3}-?\d{3}$/, func2);

就跟事件處理器是一樣的概念。上述程式碼中,兩個規則丟給同一個func2執行。

路由器模組化

路由器模組化,方法一(一般不會這樣做,所以我們只要了解有這種寫法即可):

// src/admins/admin1.js
module.exports = app => {
    app.get('/admin1/:p1?/:p2?', (req, res) => {
        res.json(req.params);
    });
};
// 在 src/index.js 內加入
const admin1 = require(__dirname + '/admins/admin1');
admin1(app);

路由器模組化,方法二:

// src/admins/admin2.js
const express = require('express'); // 在不同檔案require,實際上只會載入一次
const router = express.Router(); // 利用express的Router() function建立一個router物件
router.get('/admin2/:p1?/:p2?', (req, res) => {
    res.json(req.params);
});
module.exports = router;
// 在 src/index.js 內加入
const admin2Router = require(__dirname + '/admins/admin2');
app.use(admin2Router); //當成 middleware 使用

測試結果
image

src/admins/admin2.js修改如下:

const express = require('express');
const router = express.Router();
router.get('/admin2/:action?/:id?', (req, res) => {
    const output = {
        action: req.params.action,
        id: req.params.id,
        url: req.url,
        baseUrl: req.baseUrl
    }
    res.json(output);
})

module.exports = router;

測試結果:

另外,index.js中的

const admin2Router = require(__dirname + '/admins/admin2')
app.use(admin2Router);

也可以改寫為:

app.use(require(__dirname + '/admins/admin2'));

app.use有一個特性,就是其第一個參數可以設定baseUrl,如:
app.use('/my', require(__dirname + '/admins/admin2'));

測試結果如下:

這個設定的方便之處在於將來如果要把整個網站設定到某一個路徑之下,只要設定此處即可。比如,現在已經有一個/admin的管理功能,現在要先增一個新的/admin管理功能,但是舊的程式希望保留,以作為備用,這樣就可以在舊的路由前面加上baseUrl,/admin_old,既可以既保留舊有的功能,又可以測試新版本的功能。改名稱就可以直接抽換整個模組。

路由器模組化,方法三:

// 在 src/index.js 內加入
const admin3Router = require(__dirname + '/admins/admin3');
app.use('/admin3', admin3Router); // module 裡面的路徑用相對路徑

javascript小複習

參考:Javascript淺層複製與深層複製

image


image


image


image

另一個練習

// src/admins/admin3.js
const express = require('express');
const router = express.Router();
router.route('/member/edit/:id')
    .all((req, res, next) => {
        // 找到該會員資料
        res.locals.memberData = {
            name: 'benctw',
            id: '386'
        };
        next();
    })
    .get((req, res) => {
        const obj = {
            baseUrl: req.baseUrl, // 查看基底 url
            url: req.url,
            data: res.locals.memberData
        };
        res.send('get edit:' + JSON.stringify(obj));
    })
    .post((req, res) => {
        res.send('post edit:' + JSON.stringify(res.locals.memberData));
    });
module.exports = router;

這邊可以注意:

    .all((req, res, next) => {
        // 找到該會員資料
        res.locals.memberData = {
            name: 'benctw',
            id: '386'
        };
        next();
    })

.all就是對任何協定(如:GET, POST, PUT, DELETE)採取動作,處理完畢後,再使用next()去執行接下來的程式。一般可以用於top-level middleware中。關於express.js的middleware的說明可以參考這裡,有詳細的說明

原本index.js中有的top-level middleware是:

// top-level middleware
app.use(express.urlencoded({ extended: false }));
app.use(express.json());

可以再新增如下的top-level middleware以做預處理:

app.use((req, res, next) => {
    // 要預處理的事情
    res.locals.userData = {
        name: 'benctw',
        id: 386,
        action: 'edit'
    }
    next();
});

既然我們已經新增了res.locals的資料,剛剛的admin2.js就可以修改為:

const express = require('express');
const router = express.Router();
router.get('/admin2/:action?/:id?', (req, res) => {
    const output = {
        action: req.params.action,
        id: req.params.id,
        url: req.url,
        baseUrl: req.baseUrl,
        locals: res.locals
    }
    res.json(output);
})

module.exports = router;

瀏覽 http://localhost:3000/my/admin2/abc/123 可以得到: