#1 middlewares, route, views, request 和response對象

Express是建立在NODEJS http模塊基礎上的網(wǎng)絡服務框架,具有以下幾個特點:

  • 使用JavaScript作為開發(fā)語言,實現(xiàn)前端后端語言的一致性
  • 最大特點:中間件 和 路由
  • 靈活性,由于采用模塊化的理念,可以根據(jù)需求對服務進行組裝

express的基本概念,可以參見express官網(wǎng), 其核心內容很少。下面是自己學習的一些筆記。

一.第三方middleware庫

morgan: LOGGING MIDDLEWARE

這個用于記錄日志使用,可用于記錄用戶操作,服務端響應請求requests花費時間,用作性能分析。

安裝:

npm install --save morgan

使用:

var express = require('express');
var http = require('http');
// 引入 Morgan 模塊
var logger = require('morgan');

var app = express();

// 使用morgan中間件, 并規(guī)定輸出格式
// 還可以設置為其它選項,比如 app.use(logger('dev'));
app.use(logger('short'));

app.use(function(req, res) {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('hello express');
});

http.createServer(app).listen(3000);

// 登錄 localhost:3000
// console中顯示
::1 - GET / HTTP/1.1 200 - - 0.621 ms

express.static

這個是express自身封裝的一個中間件,所以無需從npm中安裝。這個中間件用于發(fā)送靜態(tài)資源,比如images, CSS 或HTML文件等。

假如應用的靜態(tài)資源存放在 public 目錄下,則可以通過此中間件發(fā)送文件:

var path = require('path');

// 使用NODE的 path模塊設置 public 路徑
var publicPath = path.resolve(__dirname, 'public');

// 聲明 'public' 目錄為靜態(tài)文件夾
app.use(express.static(publicPath));

app.use(function(req, res) {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('沒有找到該靜態(tài)文件');
});
// ...

如果沒有找到文件,則app將進入下一個中間件,如果找到了,express.static將發(fā)送該文件,并且停止中間件的鏈式操作。

值得之一的是,開發(fā)過程中,可以使用 res.sendFile() 提供靜態(tài)文件,但是在生產環(huán)境中不要執(zhí)行此函數(shù),因為它必須針對每次文件請求讀取文件系統(tǒng),會產生嚴重的延遲,影響應用程序的總體性能。 建議使用 serve-static 中間件,這個中間件經過優(yōu)化,用于為Express應用程序提供文件。 還有一個更好的選項,就是使用逆向代理提供靜態(tài)文件。

其它

暫時先接觸到這些中間件,其它的一些中間件僅作列舉,以后碰到再詳談:

  • body-parser: 解析HTTP request body
  • connect-ratelimit: 限制每小時連接數(shù)量,如果某人向服務器發(fā)送大量請求,這利用這個中間件返回錯誤,阻止服務器宕機
  • helmet: 給應用添加 HTTP headers, 用于阻止特定類型的攻擊
  • cookie-parser: 解析瀏覽器cookies
  • response-time: 發(fā)送 X-Response-Time header,用于調試app性能

二.Routing

路由根據(jù) URL 和特定的 HTTP method 將請求(requests)匹配到特定的處理函數(shù)(handlers).

例如:

var express = require('express');
var http = require('http');
var path = require('path');

var app = express()
var publicPath = path.resolve(__dirname, 'public');
app.use(express.static(publicPath));

// 對于 '/' 表示根目錄
// HTTP GET 請求
app.get('/', function(req, res) {
    res.end('這是主頁');
});


// 動態(tài)的路徑匹配 '/about/:name'
app.get('/about/:name', function(req, res) {
    res.end(`${req.params.name} 的About頁面`)
});

// 如果沒有匹配到,則返回404
// 使用中間件
app.use(function(req, res) {
    // 將狀態(tài)碼設置為 404
    res.statusCode = 404;
    res.end('沒有找到該頁面')
});

http.createServer(app).listen('3000');

除了可以使用 app.get(), 還可以使用其它的http動作,比如 app.post()等等。

路由的路徑匹配有3中方式:

  1. 字符串
  2. 字符串模式: 其實就是正則表達式和字符串的一種結合寫法
  3. 正則表達式

1.最常見的字符串:

app.get('/home', ...)
app.get('/about/company', ...)
app.get('/user/:id', ...) //  匹配 /user/1, /user/2, ....

2.字符串模式

app.get('/ab?cd', ...)   // 匹配 acd, abcd
app.get('/ab+cd', ...)   // 匹配 abcd, abbcd, abbb...cd
app.get('/ab(cd)?e',...) // 匹配 abe, abcde

3.正則表達式

app.get(/a/, ...) // 路徑中所有具有 'a' 的路由
app.get(/.*fly$/, ...) // 任意以 'fly' 結尾的路由

三.request對象和response對象

這2個對象在Express中得到了擴充,在NODEJS中有些屬性是沒有的,下面羅列一些Express對這2個對象的擴充屬性:

res.redirect([status,] path)

response對象添加了重定向方法,狀態(tài)碼默認為302, 有以下幾種形式:

res.redirect('/foo/bar')
res.redirect('http://example.com')
res.redirect(301, 'http://example.com')
res.redirect('..')
res.redirect('back')

# 1.完全匹配URL重定向到另一個網(wǎng)址
res.redirect('http://google.com');

# 2.相對host根目錄,比如現(xiàn)在在 ' http://example.com/admin/post/new'
// 下面可以重定向到 'http://example.com/admin'
res.redirect('/admin')

# 3.可以相對當前目錄,比如現(xiàn)在在 'http://example.com/blog/admin/'(注意最后有 '/')
// 下面可以重定向到 ' http://example.com/blog/admin/post/new'
res.redirect('post/new');
// 如果當前在 'http://example.com/blog/admin'(注意最后沒有 '/')
// 下面可以重定向到 ' http://example.com/blog/post/new'

# 4.相對路徑也是可能的,比如現(xiàn)在在 'http://example.com/admin/post/new'
// 下面可以重定向到 ' http//example.com/admin/post'
res.redirect('..')

# 5.'back' 返回到 referer; 如果referer不存在,默認為 '/'
res.redirect('back')

res.sendFile(path[, options][,fn])

Express v4.8.0版本以上支持此方法。

發(fā)送指定path的文件, path 必須是一個絕對路徑。 Content-Type 根據(jù)文件的擴展設置。

options具體選項參見文檔 sendFile()

fn 必須是通過終止request-response cycle或將控制傳遞給下一個route

app.get('/file/:name', function(req, res, next) {
    var options = {
        root: __dirname + '/public',
        dotfiles: 'deny',
        headers: {
            'x-timestamp': Date.now(),
            'x-send': true
        }
    };

    var fileName = req.params.name;

    res.sendFile(fileName, options, function(err) {
        if (err) {
            next(err);
        } else {
            console.log('Send: ' + fileName);
        }
    });
});

res.status(code)
設置響應的 HTTP status,返回值可鏈式調用

res.status(403).end()
res.status(400).send('BAD REQUEST')
res.status(404).sendFile('/absolute/path/to/404.html')

res.render(view[,locals][,callback])

渲染view,并且發(fā)送已渲染的html字符串到客戶端,參數(shù):

  • locals: 一個對象,其屬性定義view中的本地變量

  • callback: 返回可能的錯誤和渲染的字符串,當發(fā)生錯誤,方法內部調用next(err)

  • view: 文件路徑字符串。可以是絕對路徑或相對 views 設置的相對路徑。如果路徑不包含文件擴展,則根據(jù) view engine 設置來決定文件擴展

local variable cache 使view緩存。設置為true,則開發(fā)時將緩存。對產品階段,view caching默認為true

// 發(fā)送渲染的view到客戶端
res.render('index')

// 如果回調指定,則渲染的html字符串必須顯式的發(fā)送
res.render('index', function(err, html) {
    res.send(html);
});

// 傳遞一個local變量給view
res.render('user', { name: 'Tobi' }, function(err, html) {
    // ...
})

req.get(field)

請求新增的方法,返回特定的 HTTP request header字段(不區(qū)分大小寫)

req.get('Content-Type') // 'text/plain'
req.get('something')    // undefined

req.ip

返回請求的ip地址

var EVIL_IP = '123.45.67.89';
app.use(function(req, res, next) {
    if (req.ip === EVIL_IP) {
        res.status(401).send('不許該ip訪問');
    } else {
        next();
    }
});
// ...

四.Views

express 的視圖可以采用多種模版來渲染,比如EJS(Embedded JavaScript), Pug, Handlebars等。

下面方式來設置視圖:

// 設置視圖所在文件夾為 views
app.set('views', path.resolve(__dirname, "views"))

// 設置視圖渲染引擎
// 這些引擎需要自己安裝 npm install --save ejs
app.set('view engine', 'ejs')

五.示例

下面是留言本的一個小網(wǎng)頁:

// app.js

var express = require('express');
var http = require('http');
var path = require('path');
var logger = require('morgan');
var bodyParser = require('body-parser');

var app = express();

// 設置視圖文件夾和渲染引擎
app.set('views', path.resolve(__dirname, 'views'));
app.set('view engine', 'ejs');

var entries = [];
app.locals.entries = entries; // 使entries在所有的views中都可用

app.use(logger('dev'));

// 如果用戶提交表單。產生一個 req.body 變量
// extended 選項是必須的
app.use(bodyParser.urlencoded({ extended: false }));

// 訪問根路徑,返回 views/index.ejs
app.get('/', function(req, res) {
    res.render('index');
});

app.get('/new-entry', function(req, res) {
    res.render('new-entry');
});

// 定義路由處理 '/new-entry' 下的 POST 請求
app.post('/new-entry', function(req, res) {
    // 如果用戶沒有填寫title或body,則返回400錯誤
    if (!req.body.title || !req.body.body) {
        res.status(400).send('Entries must have a title and body');
        return;
    }
    entries.push({
        title: req.body.title,
        content: req.body.body,
        published: new Date()
    });
    // 重定向到主頁查看新的entry
    res.redirect('/');
});

// 404 page
app.use(function(req, res) {
    res.status(400).render('404');
});


http.createServer(app).listen('8888');

具體代碼 express-guestbook github

總結

本章主要是Express的具體操作和部分APIs的介紹。簡單的談了Express的2大特點:middleware, route。另外還使用到視圖,視圖如何通過模版引擎進行渲染。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容