Express詳解


安裝 express

在目標文件夾下執行如下命令:

cnpm i express

安裝完后,在目標文件夾下新建index.js文件:

// 引入 express 模塊
var express = require('express');

// 創建 express 實例
var app = express();

// 響應HTTP的GET方法
app.get('/', function (req, res) {
 res.send('Hello World!');
});

// 監聽到8000端口
app.listen(8000, function () {
 console.log('Hello World is listening at port 8000');
});

然后在Node.js的命令行環境下執行“node index.js”命令,網站就運行起來了。瀏覽器訪問一下,可以輸出相應的信息。

使用

const express = require("express");
const app = express();

// 處理文件路徑的模塊
const path = require("path");

// view處理
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");

// 定義一個存放靜態資源的目錄
app.use(express.static("src"));

// 頁面路由的處理,此處表示首頁的時候處理index
const index = require("./routes/index");
app.use("/", index);

const server = app.listen(8080, function(){
	console.log("啟動成功!");
})

Express中的路由與路由器

Express中的路由器分為兩種類型:

app類型的路由器常使用如下代碼創建:

var express = require('express');
var app = express();

router類型的路由器常使用如下代碼創建:

var express = require('express');
var router = express.Router();

 app和router是形為function(request, response, next)形式的函數對象,使用app.verb(),router.verb()形式函數實現路由注冊(路由注冊本質上是一個觀察者模式)。

app.verb()和router.verb()中的verb常使用use、get、post、put、delete、route等動詞,不同動詞管轄的HTTP請求方法范圍不同,這些動詞函數的參數形式常為(pathExp, handleCallback)形式:其中pathExp表示請求路徑,可為正則表達式;handleCallback為路徑映射處理函數。

app是Express框架所構建程序的請求處理入口,app可作為頂層路由器使用,在應用中也可掛載下級路由器(使用router對象)以實現分級路由。

中間件

Express 是一個自身功能極簡,完全是由路由和中間件構成一個的 web 開發框架:從本質上來說,一個 Express 應用就是在調用各種中間件。

中間件(Middleware) 就是處理HTTP請求的函數,它最大的特點就是,一個中間件處理完,再傳遞給下一個中間件。App實例在運行過程中,會調用一系列的中間件。

每個中間件可以從App實例,接收三個參數,依次為request對象(代表HTTP請求)、response對象(代表HTTP回應),next回調函數(代表下一個中間件)。每個中間件都可以對HTTP請求(request對象)進行加工,並且決定是否調用next方法,將request對象再傳給下一個中間件。

use方法

use是express注冊中間件的方法,它返回一個函數。下面是一個連續調用兩個中間件的例子。

var express = require("express");
var http = require("http");

var app = express();

app.use(function(request, response, next) {
  console.log("In comes a " + request.method + " to " + request.url);
  next();
});

app.use(function(request, response) {
  response.writeHead(200, { "Content-Type": "text/plain" });
  response.end("Hello world!\n");
});

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

上面代碼使用app.use方法,注冊了兩個中間件。收到HTTP請求后,先調用第一個中間件,在控制台輸出一行信息,然后通過next方法,將執行權傳給第二個中間件,輸出HTTP回應。由於第二個中間件沒有調用next方法,所以request對象就不再向后傳遞了。

use方法內部可以對訪問路徑進行判斷,據此就能實現簡單的路由,根據不同的請求網址,返回不同的網頁內容。

var express = require("express");
var http = require("http");

var app = express();

app.use(function(request, response, next) {
  if (request.url == "/") {
    response.writeHead(200, { "Content-Type": "text/plain" });
    response.end("Welcome to the homepage!\n");
  } else {
    next();
  }
});

app.use(function(request, response, next) {
  if (request.url == "/about") {
    response.writeHead(200, { "Content-Type": "text/plain" });
  } else {
    next();
  }
});

app.use(function(request, response) {
  response.writeHead(404, { "Content-Type": "text/plain" });
  response.end("404 error!\n");
});

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

上面代碼通過request.url屬性,判斷請求的網址,從而返回不同的內容。注意,app.use方法一共登記了三個中間件,只要請求路徑匹配,就不會將執行權交給下一個中間件。因此,最后一個中間件會返回404錯誤,即前面的中間件都沒匹配請求路徑,找不到所要請求的資源。

除了在回調函數內部判斷請求的網址,use方法也允許將請求網址寫在第一個參數。這代表,只有請求路徑匹配這個參數,后面的中間件才會生效。無疑,這樣寫更加清晰和方便。

app.use('/path', someMiddleware);

上面代碼表示,只對根目錄的請求,調用某個中間件。

因此,上面的代碼可以寫成下面的樣子。

var express = require("express");
var http = require("http");

var app = express();

app.use("/home", function(request, response, next) {
  response.writeHead(200, { "Content-Type": "text/plain" });
  response.end("Welcome to the homepage!\n");
});

app.use("/about", function(request, response, next) {
  response.writeHead(200, { "Content-Type": "text/plain" });
  response.end("Welcome to the about page!\n");
});

app.use(function(request, response) {
  response.writeHead(404, { "Content-Type": "text/plain" });
  response.end("404 error!\n");
});

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

當你不寫路徑的時候,實際上就相當於"/",就是所有網址

路由get、post這些東西,也是中間件,中間件講究順序,匹配上第一個之后,就不會往后匹配了,next函數才能夠繼續往后匹配。如下實例:

app.get("/",function(req,res,next){
    console.log("1");
    next();
});

app.get("/",function(req,res){
    console.log("2");
});

all方法和HTTP動詞方法

針對不同的請求,Express提供了use方法的一些別名。比如,上面代碼也可以用別名的形式來寫。

var express = require("express");
var http = require("http");
var app = express();

app.all("*", function(request, response, next) {
  response.writeHead(200, { "Content-Type": "text/plain" });
  next();
});

app.get("/", function(request, response) {
  response.end("Welcome to the homepage!");
});

app.get("/about", function(request, response) {
  response.end("Welcome to the about page!");
});

app.get("*", function(request, response) {
  response.end("404!");
});

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

上面代碼的all方法表示,所有請求都必須通過該中間件,參數中的“*”表示對所有路徑有效。get方法則是只有GET動詞的HTTP請求通過該中間件,它的第一個參數是請求的路徑。由於get方法的回調函數沒有調用next方法,所以只要有一個中間件被調用了,后面的中間件就不會再被調用了。

除了get方法以外,Express還提供post、put、delete方法,即HTTP動詞都是Express的方法。

這些方法的第一個參數,都是請求的路徑。除了絕對匹配以外,Express允許模式匹配。

app.get("/hello/:who", function(req, res) {
  res.end("Hello, " + req.params.who + ".");
});

上面代碼將匹配“/hello/alice”網址,網址中的alice將被捕獲,作為req.params.who屬性的值。需要注意的是,捕獲后需要對網址進行檢查,過濾不安全字符,上面的寫法只是為了演示,生產中不應這樣直接使用用戶提供的值。

如果在模式參數后面加上問號,表示該參數可選。

app.get('/hello/:who?',function(req,res) {
	if(req.params.id) {
    	res.end("Hello, " + req.params.who + ".");
	}
    else {
    	res.send("Hello, Guest.");
	}
});

set方法

set方法用於指定變量的值。

app.set("views", __dirname + "/views");

app.set("view engine", "jade");

 上面代碼使用set方法,為系統變量“views”和“view engine”指定值。

response對象

API 含義
res.app 同req.app
res.append() 追加指定HTTP頭
res.set() 在res.append()后將重置之前設置的頭
res.cookie() 設置Cookie
res.clearCookie() 清除Cookie
res.download() 傳送指定路徑的文件
res.get() 返回指定的HTTP頭
res.json() 傳送JSON響應
res.jsonp() 傳送JSONP響應
res.location() 只設置響應的Location HTTP頭,不設置狀態碼或者close response
res.redirect() 設置響應的Location HTTP頭,並且設置狀態碼302
res.send() 傳送HTTP響應
res.sendFile() 傳送指定路徑的文件 -會自動根據文件extension設定Content-Type
res.set() 設置HTTP頭,傳入object可以一次設置多個頭
res.status() 設置HTTP狀態碼
res.type() 設置Content-Type的MIME類型

(1)response.redirect方法

response.redirect方法允許網址的重定向。

response.redirect("/hello/anime");
response.redirect("http://www.example.com");
response.redirect(301, "http://www.example.com"); 

res.redirect()默認響應狀態碼是302 

可以更改這個狀態碼作為res.redirect()的第一個參數

//app.js
var express = require('express');
var app = express();

app.get('/', function(req, res){
  res.redirect(302, 'demo');
});
app.get('/demo', function(req, res){
  res.end();
});

app.listen(3000);

當在url地址欄中輸入http://localhost:3000

頁面就會重定向到http://localhost:3000/demo

(2)response.sendFile方法

response.sendFile方法用於發送文件。

response.sendFile("/path/to/anime.mp4");

(3)response.render方法

response.render方法用於渲染網頁模板。

app.get("/", function(request, response) {
  response.render("index", { message: "Hello World" });
});

上面代碼使用render方法,將message變量傳入index模板,渲染成HTML網頁。

(4)res.send()

res.send用於向客戶端響應信息 

並且它的強大之處在於可以智能的處理我們傳遞的不同類型參數

app.get('/', function(req, res, next){
  res.send('express');
});

當參數為字符串,會將響應頭Content-Type默認設置為text/html

也就是解析為html呈現在我們的頁面上

app.get('/', function(req, res){
  res.send(200);
});

當參數為數字,會自動幫我們設置響應體(狀態碼…)

app.get('/', function(req, res){
  res.send([1, 2, 3]);
});

 當參數為數組或對象,它會響應一個JSON

requst對象

API 含義
req.app 當callback為外部文件時,用於訪問express的實例
req.baseUrl 獲取路由當前安裝的URL路徑
req.body/cookies 獲得「請求主體」/ Cookies
req.fresh/stale 判斷請求是否還「新鮮」
req.hostname/ip 獲取主機名和IP地址
req.originalUrl 獲取原始請求URL
req.params 獲取路由的parameters
req.path 獲取請求路徑
req.protocol 獲取協議類型
req.query 獲取URL的查詢參數串
req.route 獲取當前匹配的路由
req.subdomains 獲取子域名
req.acceptsCharsets 返回指定字符集的第一個可接受字符編碼
req.acceptsEncodings 返回指定字符集的第一個可接受字符編碼
req.acceptsLanguages 返回指定字符集的第一個可接受字符編碼
req.accepts() 檢查可接受的請求的文檔類型
req.get() 獲取指定的HTTP請求頭
req.is() 判斷請求頭Content-Type的MIME類型

(1)request.ip

request.ip屬性用於獲得HTTP請求的IP地址。

(2)request.files

request.files用於獲取上傳的文件。

(3)req.query

req.query可以獲取請求路徑參數的對象

向服務器發送請求 http://localhost:3000/?user=tester&pass[a]=123&pass[b]=456

//app.js
var express = require('express');
var app = express();

app.get('/', function(req, res, next){
  console.log(req.query);
  console.log(req.query.user); //tester
  console.log(req.query.pass.a); //123
  console.log(req.query.pass.b); //456
  res.end();
});

app.listen(3000);

(4)req.params

req.params可以解析復雜路由規則上的屬性 
(req.param綜合了req.query和req.param的功能,但是被移除了不要使用)

向服務器發送請求 http://localhost:3000/123456

//app.js
var express = require('express');
var app = express();

app.get('/:id', function(req, res, next){
  console.log(req.params.id); //123456
  res.end();
});

app.listen(3000);

這樣不論我在根路徑后輸入的是什么都會被解析為req.params.id 

靜態資源

靜態資源就是指我們在開發中用到的css、js、img等等 

它們需要存放到一個靜態資源目錄 

當瀏覽器發出了一個非HTML文件請求 

服務器就會從這個靜態資源目錄下去查找文件 

我們一般在根目錄下創建一個public文件來存儲 

並在public中創建stylesheets、javascripts、images等文件夾 

用來存儲特定類型的資源

指定靜態資源目錄的方法上面已經提到了

var path = require('path');
app.use(express.static(path.join(__dirname, 'public')));

比如說我們的html中有這樣的代碼

<link href="/javascripts/jquery.js" rel="stylesheet" media="screen">

 那么客戶端運行發出請求

服務器就會在public的javascripts文件夾下找到jquery.js靜態資源

模板引擎

express框架默認是ejs和jade渲染模板 

路由

路由的意思就是根據不同的路徑,來指定不同的處理方法 

我們一般把不同的路由封裝進不同的模塊

首先在根目錄下創建一個文件夾routes存儲路由

現在我在routes文件夾下創建倆個路由文件index.js和users.js

修改app.js

//app.js
var express = require('express');
var path = require('path');
var app = express();

var index = require('./routes/index');
var users = require('./routes/users');

app.use('/', index);
app.use('/users', users);

app.listen(3000);

這樣表示http://localhost:3000/的路由交給index處理 
http://localhost:3000/users的路由交給users處理

下面簡單的實現一下路由

//routes/index.js
var express = require('express');
var router = express.Router();

router.get('/', function(req, res){
  res.end('index');
});

router.get('/123', function(){
  res.end(123);
});

module.exports = router;

 

//routes/users.js
var express = require('express');
var router = express.Router();

router.get('/', function(req, res) {
  res.end('users');
});

module.exports = router;

通過express.Router()創建的router就像一個mini版的app一樣

app能做的,router都能做 

只是我們把邏輯都封裝到了各個路由模塊中

上面代碼的結果:

 

body-parser中間件

其實express在3.x版本中內置了很多中間件

但是4.x版本就將出static以外的所有中間件全部抽離出來了

所以就需要我們單獨安裝

對照表如下:

Express 3.0 Express 4.0
bodyParser body-parser
compress compression
cookieSession cookie-session
logger morgan
cookieParser cookie-parser
session express-session
favicon static-favicon
response-time response-time
error-handler errorhandler
method-override method-override
timeout connect-timeout
vhost vhost
csrf csurf

剛才就提到了POST請求有所不同

不同的地方就在於我們需要body-parser這個中間件來處理數據

通過req.body來獲得數據

首先使用前不要忘記下載

npm install body-parser --save
//app.js
var express = require('express');
var bodyParser = require('body-parser');
var path = require('path');
var app = express();

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

app.get('/', function(req, res){
  res.send('<form method="POST" action="./form">\
              <input type="text" name="user">\
              <input type="submit">\
            </form>');
});
app.post('/form', function(req, res){
  console.log(req.body);
  var user = req.body.user;
  res.send('賬號: ' + user);
});

app.listen(3000);

 下面這四個方法分別用於對body內容采取不同的處理方法

bodyParser.json(options) 處理JSON數據

bodyParser.raw(options) 處理buffer數據

bodyParser.text(options) 處理文本數據

bodyParser.urlencoded(options) 處理UTF-8編碼數據

這樣我首先通過get請求獲取主頁面

提交表單向服務器發送post請求

服務器響應結果

express-generator:使用express搭建應用結構

express模塊有一個命令行工具express,可以用來生成基於express模塊的應用結構(網站結構)。

express 4.x之后,express命令被獨立出來放在了express-generator模塊中。我們用下面的命令全局安裝express這個命令行工具:

cnpm install -g express-generator

安裝完成后,在命令行環境下執行“express --version”,可以看到express的版本是4.16.1。

好了,現在我們使用express命令來創建一個默認的網站。

在命令行環境下導航到node.js這個目錄下,執行下面的命令:

express FirstExpress

然后可以看到:

仔細看上面的圖哦,它告訴了我們三類非常重要的信息:

express命令創建的網站的目錄結構以及創建的文件

安裝依賴(進入到HelloExpress下,執行npm install)

使用npm start啟動網站(express 4.x后)

按照響應的提示依次執行:

cd FirstExpress

cnpm install

npm start

很快就可以看到下面的圖:

看到上圖,說明網站已正常運行。你可以在瀏覽器里訪問http://localhost:3000,然后就可以看到這個頁面:

到這里,express框架就搭建成功!

最后附上API中文地址:https://cloud.tencent.com/developer/section/1489347


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM