/

Express, 一個流行的 Node.js 框架

Express, 一個流行的 Node.js 框架

Express 是一個用於 Node.js 的 Web 框架。Node.js 是一個用於構建網絡服務和應用程序的神奇工具。Express 基於其功能提供了易於使用的功能,以滿足 Web 服務器用例的需求。

Express 簡介

Express 是一個 Node.js Web 框架。

Node.js 是一個用於構建網絡服務和應用程序的神奇工具。

Express 基於 Node.js 的功能提供了易於使用的功能,以滿足 Web 服務器用例的需求。Express 是開源、免費、易於擴展且非常高效的。還有很多預先建立的包可供使用,您只需放入並用於執行各種操作。

安裝

您可以使用 npm 將 Express 安裝到任何項目中:

1
npm install express

或者使用 Yarn 安裝:

1
yarn add express

這兩個命令在空目錄中也可以工作,當你從頭開始創建項目時(雖然 npm 不會創建 package.json 文件,而 Yarn 創建一個基本的 package.json 文件)。

如果您從頭開始創建一個新項目,只需運行 npm inityarn init

Hello World

我們準備好創建我們的第一個 Express Web 服務器了。

這是一些代碼:

1
2
3
4
5
const express = require('express')
const app = express()

app.get('/', (req, res) => res.send('Hello World!'))
app.listen(3000, () => console.log('Server ready'))

將此代碼保存到項目根目錄中的 index.js 文件中,然後使用以下命令啟動服務器

1
node index.js

您可以在本地打開瀏覽器,訪問 http://localhost:3000,您應該會看到 Hello World! 消息。

通過理解 Hello World 代碼來學習 Express 的基本知識

以上的 4 行代碼在幕後完成了很多工作。

首先,我們將 express 包引入到 express 變量中。

通過調用 express() 方法,我們實例化了一個應用程序對象。

一旦我們有了應用程序對象,我們告訴它使用 get() 方法來聽取 / 路徑上的 GET 請求。

對於每個 HTTP 動詞,都有一個對應的方法: get()post()put()delete()patch():

1
2
3
4
5
app.get('/', (req, res) => { /* */ })
app.post('/', (req, res) => { /* */ })
app.put('/', (req, res) => { /* */ })
app.delete('/', (req, res) => { /* */ })
app.patch('/', (req, res) => { /* */ })

這些方法接受一個回調函數 - 當請求開始時調用 - 我們需要對其進行處理。

我們傳入一個箭頭函數:

1
(req, res) => res.send('Hello World!')

Express 在這個回調函數中向我們發送兩個對象,我們稱之為 reqres,它們分別代表請求和響應對象。

req 是 HTTP 請求。它提供了所有請求信息,包括請求參數、標頭、請求主體等等。

res 是 HTTP 響應對象,我們將向客戶端發送它。

在這個回調函數中我們做的是將 Hello World! 字符串發送給客戶端,使用 Response.send() 方法。

這個方法將該字符串設置為主體,並且關閉連接。

示例的最後一行實際上啟動了服務器,並告訴它在端口 3000 上監聽。我們傳入一個回調函數,當服務器準備好接受新請求時調用。

請求參數

我提到了請求對象保存了所有的 HTTP 請求信息。

這是您可能會使用的主要屬性:

屬性 描述
.app 保存對 Express 應用程序對象的引用
.baseUrl 應用程序響應的基本路徑
.body 包含提交的請求主體的數據(必須手動解析和填充才能訪問)
.cookies 包含請求中的 cookies(需要 cookie-parser 中間件)
.hostname Host HTTP 標頭 中定義的主機名
.ip 客戶端 IP
.method 使用的 HTTP 方法
.params 路由命名參數
.path URL 路徑
.protocol 請求的協議
.query 一個包含請求中所有查詢字符串的對象
.secure 如果請求是安全的(使用 HTTPS) 則為 true
.signedCookies 包含請求中的簽名 cookies(需要 cookie-parser 中間件)
.xhr 如果請求是 XMLHttpRequest,則為 true

發送響應

在 Hello World 的示例中,我們使用了 Response.send() 方法來發送一個簡單的字符串作為響應,並關閉連接:

1
(req, res) => res.send('Hello World!')

如果您傳入一個字符串,它將將 Content-Type 標頭設置為 text/html

如果您傳入一個對象或數組,它將將 application/json Content-Type 標頭設置並將該參數解析為 JSON

send() 方法會自動設置 Content-Length HTTP 響應標頭。

send() 也會自動關閉連接。

發送 JSON 響應

Response.json() 方法接受對象或數組,並在發送之前將其轉換為 JSON:

1
res.json({ username: 'Flavio' })

使用 end() 發送空響應

在不包含主體的情況下發送響應的另一種方法是使用 Response.end() 方法:

1
res.end()

更改任何 HTTP 標頭值

您可以使用 Response.set() 更改任何 HTTP 標頭的值:

1
res.set('Content-Type', 'text/html')

Content-Type 標頭有一個快捷方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
res.type('.html')
// => 'text/html'

res.type('html')
// => 'text/html'

res.type('json')
// => 'application/json'

res.type('application/json')
// => 'application/json'

res.type('png')
// => image/png:

使用 Response.cookie() 方法來操作 cookie。

示例:

1
res.cookie('username', 'Flavio')

這個方法接受一個包含各種選項的第三個參數:

1
2
3
res.cookie('username', 'Flavio', { domain: '.flaviocopes.com', path: '/administrator', secure: true })

res.cookie('username', 'Flavio', { expires: new Date(Date.now() + 900000), httpOnly: true })

您可以設置的最有用的參數是:

描述
domain cookie 域名
expires 設置 cookie 失效日期。如果缺少或為 0,則該 cookie 是一個會話 cookie
httpOnly 將 cookie 設置為只能由 Web 服務器訪問。請參閱 HttpOnly
maxAge 相對於當前時間的過期時間,以毫秒為單位
path cookie 路徑。默認為 /
secure cookie 設置為僅使用 HTTPS
signed 將 cookie 設置為已簽名
sameSite SameSite 的值

可以使用以下方法清除 cookie:

1
res.clearCookie('username')

設置 HTTP 響應狀態

使用 Response.status():

1
res.status(404).end()

或者

1
res.status(404).send('File not found')

sendStatus() 是一種快捷方式:

1
2
3
4
5
6
7
8
9
10
11
res.sendStatus(200)
// === res.status(200).send('OK')

res.sendStatus(403)
// === res.status(403).send('Forbidden')

res.sendStatus(404)
// === res.status(404).send('Not Found')

res.sendStatus(500)
// === res.status(500).send('Internal Server Error')

處理重定向

重定向在 Web 開發中很常見。您可以使用 Response.redirect() 方法來創建重定向:

1
res.redirect('/go-there')

這創建了一個 302 重定向。

301 重定向可以這樣做:

1
res.redirect(301, '/go-there')

您可以指定絕對路徑(/go-there)、絕對 URL (https://anothersite.com)、相對路徑(go-there),或者使用 .. 返回一級:

1
2
res.redirect('../go-there')
res.redirect('..')

您還可以使用 Referer HTTP 標頭值(如果未設置,則默認為 /) 將重定向返回到前一個 URL,使用以下方法:

1
res.redirect('back')

發送文件供下載

Response.download() 方法允許您發送一個附加到請求的文件。瀏覽器將保存該文件而不是顯示該頁面。

1
2
3
res.download('/file.pdf')

res.download('/file.pdf', 'user-facing-filename.pdf')

支持 JSONP

JSONP 是一種從客戶端 JavaScript 使用跨源 API 的方法。

您可以使用 Response.jsonp() 方法來支持 JSONP,這與 Response.json() 相似,但處理了 JSONP 調用:

1
res.jsonp({ username: 'Flavio' })

路由

在 Hello World 的示例中,我們使用了以下代碼:

1
app.get('/', (req, res) => { /* */ })

這創建了一個將根域名路徑 / 映射為我們要提供的響應的路由。

命名參數

如果我們想要聽取自定義請求怎麼辦?也許我們想要創建一個接受字符串並將其返回為大寫的服務。如果我們不希望將參數作為查詢字符串發送,而是作為 URL 的一部分,我們可以使用命名參數:

1
app.get('/uppercase/:theValue', (req, res) => res.send(req.params.theValue.toUpperCase()))

如果我們向 /uppercase/test 發送請求,我們將在響應的主體中得到 TEST

您可以在同一個 URL 中使用多個命名參數,它們都將存儲在 req.params 中。

使用正則表達式匹配路徑

您可以使用正則表達式來匹配多個路徑:

1
app.get(/post/, (req, res) => { /* */ })

這將匹配 /post/post/first/thepost/posting/something 等等。

中間件

中間件是一個函數,它鉤入路由過程,在某些時候執行操作,具體取決於我們希望它做的事情。

通常用於編輯請求或響應對象,或者在它達到路由處理程序代碼之前終止請求。

用法如下:

1
app.use((req, res, next) => { /* */ })

這與定義路由相似,但除了 Request 和 Response 對象實例之外,我們還將下一個中間件函數的引用,分配給變量 next

我們總是在我們的中間件函數的末尾調用 next(),以將執行傳遞給下一個處理程序,除非我們想要提前結束響應並將其返回給客戶端。

通常情況下,您使用預製的中間件,以 npm 包的形式。可用的中間件列表可以在這裡找到: https://expressjs.com/en/resources/middleware.html

一個例子是 cookie-parser,它用於將 cookie 解析為 req.cookies 對象。您可以使用 npm install cookie-parser 安裝它,然後像這樣使用它:

1
2
3
4
5
6
7
8
const express = require('express')
const app = express()
const cookieParser = require('cookie-parser')

app.get('/', (req, res) => res.send('Hello World!'))

app.use(cookieParser())
app.listen(3000, () => console.log('Server ready'))

您還可以為特定路由設置中間件函數,將其用作路由定義的第二個參數:

1
2
3
4
5
6
const myMiddleware = (req, res, next) => {
/* ... */
next()
}

app.get('/', myMiddleware, (req, res) => res.send('Hello World!'))

如果您需要將在中間件中生成的數據存儲以便將其傳遞給後續的中間件函數或請求處理程序,您可以使用 Request.locals 對象。它將數據附加到當前請求:

1
req.locals.name = 'Flavio'

提供靜態資源

通常在 public 子文件夾中有圖片、CSS 等文件,並將它們暴露到根級別:

1
2
3
4
5
6
7
8
const express = require('express')
const app = express()

app.use(express.static('public'))

/* ... */

app.listen(3000, () => console.log('Server ready'))

如果在 public/ 中有一個 index.html 文件,則將它提供給您現在訪問的根域名(http://localhost:3000)

模板

Express 能夠處理服務器端模板。

Jade 是默認模板引擎,但您可以使用許多不同的模板引擎,包括 Pug、Mustache、EJS 等等。

假設我不喜歡 Jade(我確實不喜歡),想要改用 Handlebars。

您可以使用 npm install hbs 安裝它。

about.hbs 模板文件放在 views/ 文件夾中:

1
Hello from {{name}}

然後使用以下 Express 配置在 /about 上提供它:

1
2
3
4
5
6
7
8
9
10
11
const express = require('express')
const app = express()
const hbs = require('hbs')

app.set('view engine', 'hbs')

app.get('/about', (req, res) => {
res.render('about', { name: 'Flavio' })
})

app.listen(3000, () => console.log('Server ready'))

您還可以使用 express-react-views 套件 在服務器端渲染 React 應用程序

首先使用 npm install express-react-views react react-dom

現在不使用 hbs,而是使用 express-react-views,並使用 jsx 文件作為引擎:

1
2
3
4
5
6
7
8
9
10
11
const express = require('express')
const app = express()

app.set('view engine', 'jsx')
app.engine('jsx', require('express-react-views').createEngine())

app.get('/about', (req, res) => {
res.render('about', { name: 'Flavio' })
})

app.listen(3000, () => console.log('Server ready'))

只需在 views/ 中放一個 about.jsx 文件,訪問 /about 應該會顯示 “Hello from Flavio” 字符串:

1
2
3
4
5
6
7
8
9
const React = require('react')

class HelloMessage extends React.Component {
render() {
return <div>Hello from {this.props.name}</div>
}
}

module.exports = HelloMessage

下一步?

Express 在內部具有 Node.js 的所有強大功能。

您可以做任何您想要的事情,包括連接到數據庫、使用任何類型的緩存、Redis、使用 sockets,以及您可以在服務器上想像的任何操作。

只有天空是極限。