深入理解express框架
寫在前面
Express 是一個簡潔而靈活的 node.js Web應用框架, 提供了一系列強大特性幫助你建立各種 Web 應用,和豐富的 HTTP 工具。使用 Express 可以快速地搭建一個完整功能的網站。http://jafeney.com/2016/01/10/2016-01-10-express/
Express 框架核心特性:
- 可以設定中介軟體來響應 HTTP 請求。
- 定義了路由表用於執行不同的 HTTP 請求動作。
- 可以通過向模板傳遞引數來動態渲染 HTML頁面。
今天不介紹基本用法,如何用它搭建nodeJS中間層在我前面的文章裡有比較詳細的介紹,而這次主要是深入研究它的內部實現原理。
底層HTTP伺服器
學習nodeJS基礎的時候我們經常要用到 http
這個核心模組,它以極其簡潔JavaScript程式碼
搭建出本地http服務.
1234567891011121314 | // 引入所需模組var http = require("http");// 建立伺服器var app = http.createServer(function(request, response) { response.writeHead(200, { "Content-Type": "text/plain" }); response.end("Hello world!\n");});// 啟動伺服器app.listen(1337, "localhost");console.log("Server running at http://localhost:1337/"); |
解析: 第一行使用
require
函式引入Node內建模組http
。然後存入名為http
的變數中。然後我們使用http.createServer()
將伺服器儲存至 app 變數。它將一個函式作為引數監聽請求。稍後將會詳細介紹它。最後我們要做的就是告訴伺服器監聽來自1337埠的請求,之後輸出結果。然後一切完成。
中介軟體
通過中介軟體,搭建本地伺服器的過程變得簡單,比如最簡單的connect
123456789101112131415 | // 引入所需模組var connect = require("connect");var http = require("http");// 建立appvar app = connect();// 新增中介軟體app.use(function(request, response) { response.writeHead(200, { "Content-Type": "text/plain" }); response.end("Hello world!\n");});// 啟動應用http.createServer(app).listen(1337); |
express 4.x 已經把connect中介軟體封裝的express模組裡。因此我們不需要依賴connect模組:
123456 | var express = require('express');var app = express();var server = app.listen(8081, function () { console.log("Server running at http://localhost:1337/")}); |
什麼是中介軟體
一個最基本的中介軟體結構如下:
12345 | function myFunMiddleware(request, response, next) { // 對request和response作出相應操作 // 操作完畢後返回next()即可轉入下個中介軟體 next();} |
當我們啟動一個伺服器,函式開始從頂部一直往下執行。還是還來個能跑的程式
12345678910111213141516171819202122232425262728293031323334353637 | var connect = require("connect");var http = require("http");var app = connect();app.use(connect.logger());// Homepageapp.use(function(request, response, next) { if (request.url == "/") { response.writeHead(200, { "Content-Type": "text/plain" }); response.end("Welcome to the homepage!\n");// The middleware stops here. } else { next(); }});// About pageapp.use(function(request, response, next) { if (request.url == "/about") { response.writeHead(200, { "Content-Type": "text/plain" }); response.end("Welcome to the about page!\n");// The middleware stops here. } else { next(); }});// 404'd!app.use(function(request, response) { response.writeHead(404, { "Content-Type": "text/plain" }); response.end("404 error!\n");});http.createServer(app).listen(1337); |
是的,這樣的過程略顯繁瑣,因此express
把connect封裝到了自己模組裡,用express實現上面的過程簡單了不少(往下看)
。
請求和響應
Request 物件
request 物件表示 HTTP 請求,包含了請求查詢字串,引數,內容,HTTP 頭部等屬性。常用屬性和方法有16項
req.app
當callback為外部檔案時,用req.app訪問express的例項。req.baseUrl
獲取路由當前安裝的URL路徑req.body
/req.cookies
獲得「請求主體」/ Cookiesreq.fresh
/req.stale
判斷請求是否還「新鮮」req.hostname
/req.ip
獲取主機名和IP地址req.originalUrl
獲取原始請求URLreq.params
獲取路由的parametersreq.path
獲取請求路徑req.protocol
獲取協議型別req.query
獲取URL的查詢引數串req.route
獲取當前匹配的路由req.subdomains
獲取子域名req.accpets()
檢查請求的Accept頭的請求型別req.acceptsCharsets
/req.acceptsEncodings
/req.acceptsLanguages
req.get()
獲取指定的HTTP請求頭req.is()
判斷請求頭Content-Type的MIME型別
Response 物件
response 物件表示 HTTP 響應,即在接收到請求時向客戶端傳送的 HTTP 響應資料。常見屬性有以下17項。
res.app
同req.app一樣res.append()
追加指定HTTP頭res.set()
在res.append()
後將重置之前設定的頭res.cookie(name,value [,option])
設定Cookieopition
domain
/expires
/httpOnly
/maxAge
/path
/secure
/signed
res.clearCookie()
清除Cookieres.download()
傳送指定路徑的檔案res.get()
返回指定的HTTP頭res.json()
傳送JSON響應res.jsonp()
傳送JSONP響應res.location()
只設置響應的Location HTTP頭,不設定狀態碼或者close responseres.redirect()
設定響應的Location HTTP頭,並且設定狀態碼302res.send()
傳送HTTP響應res.sendFile(path [,options] [,fn])
傳送指定路徑的檔案 -會自動根據檔案extension設定Content-Typeres.set()
設定HTTP頭,傳入object可以一次設定多個頭res.status()
設定HTTP狀態碼res.type()
設定Content-Type的MIME型別
路由
最原始的路由:
12345678910111213141516171819202122 | var http = require("http");http.createServer(function(req, res) {// Homepage if(req.url == "/") { res.writeHead(200, { "Content-Type": "text/html" }); res.end("Welcome to the homepage!"); }// About page else if (req.url == "/about") { res.writeHead(200, { "Content-Type": "text/html" }); res.end("Welcome to the about page!"); }// 404'd! else { res.writeHead(404, { "Content-Type": "text/plain" }); res.end("404 error! File not found."); }}).listen(1337, "localhost"); |
express封裝的路由:
1234567891011121314151617181920212223242526272829303132333435363738 | var express = require('express');var app = express();// 主頁輸出 "Hello World"app.get('/', function (req, res) { console.log("主頁 GET 請求"); res.send('Hello GET');})// POST 請求app.post('/', function (req, res) { console.log("主頁 POST 請求"); res.send('Hello POST');})// /del_user 頁面響應app.delete('/del_user', function (req, res) { console.log("/del_user 響應 DELETE 請求"); res.send('刪除頁面');})// /list_user 頁面 GET 請求app.get('/list_user', function (req, res) { console.log("/list_user GET 請求"); res.send('使用者列表頁面');})// 對頁面 abcd, abxcd, ab123cd, 等響應 GET 請求app.get('/ab*cd', function(req, res) { console.log("/ab*cd GET 請求"); res.send('正則匹配');})var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("應用例項,訪問地址為 http://%s:%s", host, port)}); |
檢視
express結合前端模板引擎可以在服務端實現檢視的渲染,這裡還是以 jade模板引擎為例:
123456789 | // 啟動Expressvar express = require("express");var app = express();// 設定view目錄app.set("views", __dirname + "/views");// 設定模板引擎app.set("view engine", "jade"); |
開頭部分的程式碼和前面基本一樣。之後我們指定檢視檔案所在目錄。然後告訴Express我們要使用 Jade作為模板引擎。
接下來 我們建立一個名為 index.jade 的檔案並把它放入 views 目錄。程式碼如下:
12345 | doctype 5html body h1 Hello, world! p= message |
我們需要從Express中渲染這個檢視。程式碼如下:
123 | app.get("/", function(request, response) { response.render("index", { message: "I love anime" }); }); |
Express為 response 物件添加了一個 render 方法。這個方法可以處理很多事情,但最主要的還是載入模板引擎和對應的檢視檔案,之後渲染成普通的HTML文件,例如這裡的 index.jade.