偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

花 15 分鐘把 Express.js 搞明白,全棧沒(méi)有那么難

開(kāi)發(fā) 前端
本文列舉了 Express 框架的核心,并舉例如何應(yīng)用,整體并沒(méi)有那么難。掌握這部分知識(shí),可以快速擁 API 開(kāi)發(fā)的思維。

大家好,我是楊成功。

Express 是老牌的 Node.js 框架,以簡(jiǎn)單和輕量著稱(chēng),幾行代碼就可以啟動(dòng)一個(gè) HTTP 服務(wù)器。市面上主流的 Node.js 框架,如 Egg.js、Nest.js 等都與 Express 息息相關(guān)。

Express 框架使用標(biāo)準(zhǔn) Node.js 語(yǔ)法,主要由以下 3 個(gè)核心部分組成:

  • 路由。
  • 中間件。
  • 錯(cuò)誤處理。

認(rèn)識(shí)基本結(jié)構(gòu)

Express 的基本結(jié)構(gòu)很簡(jiǎn)單,只需要三行代碼,應(yīng)用就可以運(yùn)行起來(lái)。

const express = require('express')
const app = express()

app.listen(9000, () => console.log('啟動(dòng)成功'))

假設(shè)上述代碼寫(xiě)在 index.js 中,我們啟動(dòng)該應(yīng)用使用命令 node ./index.js,控制臺(tái)會(huì)輸出“啟動(dòng)成功”。

為了方便,我們也可以在 package.json 中創(chuàng)建快捷命令,如下:

// package.json
{
  "scripts": {
    "start": "node ./index.js"
  }
}

那么現(xiàn)在啟動(dòng)應(yīng)用就可以用 npm run start 命令。

不過(guò)這種方式在本地運(yùn)行項(xiàng)目時(shí)會(huì)有一個(gè)弊端,就是修改文件后不會(huì)立即生效,需要重新啟動(dòng)。為了提高效率,一般會(huì)使用一個(gè)名為 PM2 的模塊啟動(dòng) Node.js 應(yīng)用。

首先全局安裝 pm2:

$ npm install -g pm2

安裝后在項(xiàng)目目錄下創(chuàng)建啟動(dòng)配置文件 ecosystem.config.js,代碼如下:

module.exports = {
  apps: [
    {
      name: 'first-api',
      script: './index.js',
    },
  ],
}

然后在項(xiàng)目目錄下執(zhí)行以下命令就可以啟動(dòng)項(xiàng)目了:

$ pm2 start --watch

圖片

上圖中的 0 就是啟動(dòng)應(yīng)用的 ID,下面會(huì)用到。

PM2 常用大命令如下:

  • pm2 start:?jiǎn)?dòng)應(yīng)用,--watch 表示監(jiān)聽(tīng)文件修改自動(dòng)重啟。
  • pm2 list:查看已啟動(dòng)的應(yīng)用列表。
  • pm2 logs <id>:查看日志輸出。
  • pm2 delete <id>:刪除指定應(yīng)用。

應(yīng)用啟動(dòng)后監(jiān)聽(tīng) 9000 端口,但訪問(wèn) “http://localhost:9000” 卻沒(méi)有反應(yīng),請(qǐng)求被掛起,這是因?yàn)闆](méi)有設(shè)置如何處理請(qǐng)求。

Express 中通過(guò)定義路由來(lái)處理請(qǐng)求。

使用路由創(chuàng)建 API 接口

路由用于定義如何處理請(qǐng)求,定義方式采用以下結(jié)構(gòu):

app.METHOD(PATH, HANDLER)

其中 app 表示 Express 的實(shí)例,其余的三個(gè)部分都屬于路由配置,表示的含義如下:

  • METHOD:路由方法。
  • PATH:路由地址。
  • HANDLER:路由處理函數(shù)。

比如示例代碼中的路由是這樣子:

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

使用app.get()定義了一個(gè) GET 請(qǐng)求的路由,第一個(gè)參數(shù) “/” 為路由地址,第二個(gè)參數(shù)為路由處理函數(shù),是一個(gè)回調(diào)函數(shù),該函數(shù)接受兩個(gè)參數(shù)分別表示請(qǐng)求和響應(yīng)。

當(dāng)路由方法和路由地址匹配到用戶(hù)請(qǐng)求時(shí),路由處理函數(shù)就會(huì)執(zhí)行。

路由方法根據(jù)基本 API 規(guī)則支持五種,分別如下:

  • app.get():GET 請(qǐng)求。
  • app.post():POST 請(qǐng)求。
  • app.put():PUT 請(qǐng)求。
  • app.delete():DELETE 請(qǐng)求。
  • app.all():匹配所有請(qǐng)求。

以上五個(gè)方法的參數(shù)都與示例路由一致。定義好路由后,我們的主要任務(wù)是在路由處理函數(shù)中編寫(xiě)業(yè)務(wù)代碼,一般會(huì)包括接收請(qǐng)求參數(shù)、返回接口響應(yīng),這里要用到路由處理函數(shù)的兩個(gè)參數(shù)。

請(qǐng)求對(duì)象

路由處理函數(shù)的第一個(gè)參數(shù)表示請(qǐng)求對(duì)象,包含客戶(hù)端請(qǐng)求攜帶的相關(guān)數(shù)據(jù),常用的屬性如下:

  • req.query:URL 附加參數(shù)。
  • req.body:請(qǐng)求體參數(shù)。
  • req.method:請(qǐng)求方法。
  • req.headers:請(qǐng)求頭對(duì)象。
  • req.params:URL 地址參數(shù)。

現(xiàn)在我們定義一個(gè)路由,將請(qǐng)求對(duì)象的這幾個(gè)屬性返回,看一下它們的值是什么:

app.post('/first/:id', (req, res) => {
  let { method, query, body, params, headers } = req
  res.send({ method, query, body, params, headers })
})

在 Postman 中請(qǐng)求地址 “http://localhost:9000/first/8?tag=test” 并傳入請(qǐng)求體參數(shù) {data: "xxx"},請(qǐng)求結(jié)果如下:

圖片

對(duì)照請(qǐng)求參數(shù)和返回結(jié)果,可以發(fā)現(xiàn)路由地址中的 :id 占位符解析后被放到 “req.params” 對(duì)象下。地址參數(shù) ?tag=test 解析后被放到 “req.query” 對(duì)象下。

但是有一個(gè)問(wèn)題:請(qǐng)求體沒(méi)有被解析出來(lái)。

這是因?yàn)檎?qǐng)求體是按照流處理的,無(wú)法直接獲取到,我們需要一個(gè)第三方工具包協(xié)助。首先安裝如下:

$ yarn add body-parser

然后在 index.js 中引入并加載:

const bodyParser = require('body-parser')
app.use(bodyParser.json())

現(xiàn)在重新請(qǐng)求,接可以看到 req.body 的返回結(jié)果了:

圖片

響應(yīng)對(duì)象

路由處理函數(shù)的第二個(gè)參數(shù)表示響應(yīng)對(duì)象,用于向客戶(hù)端返回結(jié)果,也就是定義接口的返回值。路由處理函數(shù)中必須設(shè)置響應(yīng),否則客戶(hù)端請(qǐng)求會(huì)一直處于掛起狀態(tài),無(wú)返回值。

常用的響應(yīng)方法有以下三種,用于返回不同類(lèi)型的數(shù)據(jù):

  • res.json():發(fā)送 JSON 響應(yīng)。
  • res.render():發(fā)送視圖響應(yīng)(HTML)。
  • res.send():發(fā)送各種類(lèi)型的響應(yīng)。

我們統(tǒng)一使用 res.send() 方法響應(yīng)數(shù)據(jù)。一般在響應(yīng)前還可以通過(guò) res.status() 方法設(shè)置 HTTP 狀態(tài)碼,示例如下:

res.send('哈哈') // 狀態(tài)碼:200,返回值:"哈哈"
res.status(201).send({
  msg: 'created',
}) // 狀態(tài)碼:201,返回值:{msg:"created"}
res.status(401).send('請(qǐng)登錄') // 狀態(tài)碼:401,返回值:"請(qǐng)登錄"

發(fā)送響應(yīng)時(shí)也常常會(huì)遇到問(wèn)題,以下兩條原則請(qǐng)牢記,避免踩坑:

  • 一個(gè)路由處理函數(shù)中只能響應(yīng)一次,不能重復(fù)響應(yīng)。
  • res.send() 不能直接返回?cái)?shù)字。

分組路由

使用 app 實(shí)例注冊(cè)路由固然方便,但是如果定義的路由很多,都注冊(cè)在 app 實(shí)例下很可能會(huì)帶來(lái)全局污染,這與全局變量一個(gè)道理。為了應(yīng)用的健壯性,我們應(yīng)該將路由分組。

Express 提供了 Router 類(lèi)來(lái)創(chuàng)建模塊化的路由程序,它像一個(gè)微應(yīng)用,可以隨時(shí)被 app 實(shí)例掛載。這樣就可以把一組路由保存在一個(gè)單獨(dú)的文件中,需要時(shí)加載,從而實(shí)現(xiàn)路由分組。

創(chuàng)建一個(gè) router 文件夾用于保存路由文件,然后創(chuàng)建 router/test.js 文件,在文件中呢寫(xiě)入路由代碼,如下:

// router/test.js
var express = require('express')
var router = express.Router()

router.post('/info', (req, res) => {
  res.send('TEST 路由組')
})

module.exports = router

這樣一個(gè)基本的路由模塊就寫(xiě)好了,如果讓其生效,需要在主程序中加載該模塊:

const testRouter = require('./router/test.js')
app.use('/test', testRouter)

上述代碼表示請(qǐng)求 “/test” 時(shí)加載路由模塊,訪問(wèn)某個(gè)路由時(shí)使用該路徑拼接路由地址,像下面這樣:

http://localhost:9000/test/info
# 返回 "TEST 路由組"

為了開(kāi)發(fā)規(guī)范,我們統(tǒng)一把路由定義為路由模塊,而不直接在 app 下注冊(cè)。

理解中間件,搞懂框架原理

Express 應(yīng)用是由一系列中間件構(gòu)成的。中間件同樣是一個(gè)聽(tīng)著很玄乎的詞兒,但它的本質(zhì)就是一個(gè)函數(shù)。我們看一個(gè)中間件函數(shù)的代碼示例:

var myLogger = function (req, res, next) {
  console.log('LOGGED')
  next()
}

中間件與普通函數(shù)的區(qū)別就是它有三個(gè)參數(shù),分別表示請(qǐng)求對(duì)象(req),響應(yīng)對(duì)象(res)和一個(gè) next() 函數(shù) ——— 也許你發(fā)現(xiàn)了,路由處理函數(shù)也是這個(gè)結(jié)構(gòu)。

沒(méi)錯(cuò),路由處理函數(shù)本身就是一個(gè)中間件。

將中間件掛載到應(yīng)用上,使用 app.use() 方法:

app.use(myLogger)

看到這里你又會(huì)發(fā)現(xiàn),請(qǐng)求體解析包 body-parser 也是這么掛載的,因?yàn)樵摪彩且粋€(gè)中間件。

直接用 app.use() 掛載的中間件在收到任意請(qǐng)求時(shí)都會(huì)執(zhí)行。如果要限定執(zhí)行條件,可以添加一個(gè)路徑匹配,如下:

app.use('/test/*', myLogger)

這樣,只有以 /test 開(kāi)頭的請(qǐng)求才會(huì)執(zhí)行 myLogger 中間件,這看起來(lái)與路由注冊(cè)很相似。其實(shí)注冊(cè)路由正是這種中間件掛載方式的快捷寫(xiě)法,只不過(guò)多了一個(gè)請(qǐng)求方法的限制。

Express 應(yīng)用中一切皆中間件,如果匹配到多個(gè)中間件會(huì)按照順序依次調(diào)用。此時(shí) next() 函數(shù)就能派上用場(chǎng)了,他的作用是進(jìn)入下一個(gè)中間件。

比如代碼中的 myLogger 中間件,將它掛載到路由之前,那么每次請(qǐng)求首先會(huì)打印出 “LOGGED”,然后再進(jìn)入路由處理函數(shù)。

如果 myLogger 中間件中沒(méi)有調(diào)用 next() 函數(shù),請(qǐng)求就會(huì)被堵在這里,無(wú)法進(jìn)入路由處理函數(shù),此時(shí)請(qǐng)求會(huì)被掛起。

統(tǒng)一錯(cuò)誤處理,提升健壯性

既然一切皆中間件,那么錯(cuò)誤處理也是一個(gè)中間件。錯(cuò)誤處理函數(shù)與其他的中間件函數(shù)稍有不同,它多了一個(gè) err 參數(shù),如下:

app.use((err, req, res, next) => {
  console.error(err.stack)
  res.status(500).send('服務(wù)器出錯(cuò)了!')
})

err 參數(shù)表示錯(cuò)誤信息,當(dāng)發(fā)生錯(cuò)誤時(shí)進(jìn)入該中間件,此時(shí)要設(shè)置 HTTP 狀態(tài)碼為 500,并根據(jù)錯(cuò)誤信息為客戶(hù)端返回錯(cuò)誤響應(yīng)。

錯(cuò)誤處理中間件是一個(gè)兜底中間件,請(qǐng)確保它定義在所有中間件之后,是應(yīng)用中的最后一個(gè)中間件。

請(qǐng)求進(jìn)入錯(cuò)誤中間件,說(shuō)明前面的所有中間件都沒(méi)有匹配到。但是如果客戶(hù)端請(qǐng)求地址寫(xiě)錯(cuò)而進(jìn)入錯(cuò)誤處理中間件,此時(shí)返回 500 錯(cuò)誤顯然不合理,應(yīng)該是 404 資源未找到。

所以在錯(cuò)誤處理中間件前,還應(yīng)該定義一個(gè) 404 中間件。該中間件要在所有路由之后,錯(cuò)誤處理之前,是應(yīng)用的倒數(shù)第二個(gè)中間件,代碼如下:

app.use((req, res, next) => {
  res.status(404).send('Not Found')
})

好了,現(xiàn)在我們的應(yīng)用就健壯多了。

總結(jié)

本文列舉了 Express 框架的核心,并舉例如何應(yīng)用,整體并沒(méi)有那么難。掌握這部分知識(shí),可以快速擁 API 開(kāi)發(fā)的思維。

責(zé)任編輯:姜華 來(lái)源: 程序員成功
相關(guān)推薦

2013-06-14 09:27:51

Express.jsJavaScript

2019-02-21 10:38:10

Web 開(kāi)發(fā)代碼

2017-04-24 08:31:26

Node.jsExpress.jsHTTP

2018-07-13 11:12:03

芯片設(shè)計(jì) IC

2024-09-11 23:00:41

2021-10-11 10:41:14

TCP傳輸層協(xié)議網(wǎng)絡(luò)

2022-07-08 14:14:04

并發(fā)編程異步編程

2024-05-09 09:01:03

2021-01-14 10:48:34

Docker CompNode.js開(kāi)發(fā)

2014-03-31 15:32:36

AndroidNDK開(kāi)發(fā)

2023-03-27 09:40:01

GoWebAssembl集成

2023-07-30 16:09:38

數(shù)字經(jīng)濟(jì)數(shù)字化轉(zhuǎn)型

2022-07-11 12:37:15

安全運(yùn)營(yíng)網(wǎng)絡(luò)攻擊

2025-03-05 00:02:00

Next.jsExpress.jAPI 路由

2013-01-06 13:45:14

2024-11-22 14:24:24

模型訓(xùn)練

2018-07-30 09:00:49

技術(shù)管理實(shí)踐

2020-07-10 08:03:35

DNS網(wǎng)絡(luò)ARPAne

2019-08-27 14:46:59

ElasticSearES數(shù)據(jù)庫(kù)

2024-05-13 10:45:25

中介模式面向?qū)ο?/a>數(shù)量
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)