const express = require('express') // 引入express,返回一個(gè)函數(shù)

const app = express() // 執(zhí)行函數(shù),返回一個(gè)Express對(duì)象

/**
* express方法返回的對(duì)象有很多方法,例如:
* app.get()
* app.post()
* app.put()
* app.delete()
* ...
* 這些都對(duì)應(yīng)這HTTP的方法(get、post、put、delete...)
*/

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

app.listen(3000, () => {console.log('app runing port 3000')})

對(duì)象app的請(qǐng)求處理方法接收兩個(gè)參數(shù),第一個(gè)是路徑(URL),/代表站點(diǎn)的根目錄;第二個(gè)是回調(diào)函數(shù)(也叫路由句柄),當(dāng)我們應(yīng)用接受到對(duì)應(yīng)的請(qǐng)求并匹配對(duì)應(yīng)的路徑時(shí),調(diào)用的方法?;卣{(diào)函數(shù)有兩個(gè)參數(shù),分別是request代表請(qǐng)求對(duì)象,response響應(yīng)對(duì)象,這兩個(gè)對(duì)象有很多屬性,request對(duì)象的屬性可以讓我們了解請(qǐng)求的信息,可以先在官方文檔里查看request對(duì)象的API。最后就是使用app.listen方法監(jiān)聽指定的端口。使用node index.js啟動(dòng)應(yīng)用,在瀏覽器中輸入localhost:3000進(jìn)行訪問。

Node 監(jiān)視器

每一次修改node程序代碼,我們都要手工重新運(yùn)行,有更好的方法,我們安裝一個(gè)node包叫nodemon(node monitor)的縮寫,使用全局安裝方式安裝:

npm i -g nodemon

安裝后,以后運(yùn)行程序就是用nodemon index.js,啟動(dòng)后,nodemon會(huì)監(jiān)測(cè)該文件夾所有文件、文件名、文件拓展名的改動(dòng),在代碼修改保存后,nodemon會(huì)自動(dòng)重啟運(yùn)行應(yīng)用,這樣每次修改就不用手工重啟了。

環(huán)境變量

上面我們寫的web服務(wù)器監(jiān)聽的服務(wù)端口3000是寫死的,在開發(fā)環(huán)境還行,但是在生產(chǎn)環(huán)境可能就不靈了,因?yàn)楫?dāng)我們把應(yīng)用發(fā)布到一個(gè)共享平臺(tái)時(shí),應(yīng)用可用的端口是由平臺(tái)動(dòng)態(tài)分配的,所以不能確定我們寫的3000是否一定有用,優(yōu)化這個(gè)問題的做法是使用環(huán)境變量,一般的,在環(huán)境變量中管理端口的屬性是PORT,環(huán)境變量就是在進(jìn)程運(yùn)行時(shí)才產(chǎn)生的變量,它是在應(yīng)用之外設(shè)置的變量,在node中,需要讀取環(huán)境變量的PORT屬性,so,為了讀取PORT屬性,我們需要使用process對(duì)象。process是node中的全局對(duì)象,該對(duì)象下有個(gè)屬性是env,該屬性可以獲取系統(tǒng)設(shè)置的所有環(huán)境變量,所以我們可以使用process.env.PORT來指定應(yīng)用的端口:

// 如果系統(tǒng)指定了PORT,就用系統(tǒng)的指定的端口,如果沒有就用3000端口
const port = process.env.PORT || 3000
// 所以上面的應(yīng)用監(jiān)聽改為
app.listen(port, () => {console.log(app runing port ${port})})

設(shè)置環(huán)境變量

export PORT=5000
set PORT=5000

小結(jié)

這就是node應(yīng)用中正確設(shè)置端口的方法,需要先嘗試讀取環(huán)境變量的值,如果有,就用環(huán)境變量里的值,沒有就用設(shè)置的端口。

在 Express 中處理請(qǐng)求

這一小節(jié)我們講解在Express中處理get、post、put、delete請(qǐng)求,我們可以使用Postman來測(cè)試我們創(chuàng)建的服務(wù),Postman使用簡(jiǎn)單,可以自行在網(wǎng)上搜索使用。

處理get請(qǐng)求和獲取參數(shù)

我們以電影為例,首先創(chuàng)建一個(gè)RESTful服務(wù),獲取所有電影列表(暫時(shí)不涉及數(shù)據(jù)庫操作,數(shù)據(jù)保存在內(nèi)存中,后面再講數(shù)據(jù)庫):

const express = require('express')

const app = express()

const port = process.env.PORT || 3000
let videos = [
{id: 1, name: 'movie1'},
{id: 2, name: 'movie2'}
]
// 獲取所有電影列表
app.get('/api/videos', (req, res) => {
res.send(videos)
})

app.listen(port, () => { console.log(Listening on port ${port}) })

獲取路由參數(shù)

如果需要獲取單個(gè)電影數(shù)據(jù),就需要在url中包含電影的id,所以,如果想獲取id為1的電影,url應(yīng)該是這樣的:/api/videos/1。1是電影的id,我們的應(yīng)用中就應(yīng)該這樣寫:

// :id表示參數(shù),id是參數(shù)名,也可以叫別的名字
// 也可以指定多個(gè)參數(shù),例如:/api/videos/:year/:month/:day
app.get(/api/videos/:id, (req, res) => { // req.params.id 獲取路由參數(shù)(String類型) // 從電影列表中查找到指定單個(gè)電影 let video = videos.find(v => v.id === parseInt(req.params.id)) // 沒有找到,返回404,這是RESTful的慣例 if (!video) return res.status(404).send('no hava this video') res.send(video) // 返回指定的單個(gè)電影數(shù)據(jù) })

獲取查詢字符串

使用查詢字符串向后端服務(wù)傳遞額外的參數(shù),例如url為:https//www.zkk-pro/api/videos/1?name=movie1,表示獲取id為1的電影數(shù)據(jù),并且電影名字叫movie1的,我們用參數(shù)提供路由必要的值,使用查詢字符串傳遞額外的內(nèi)容,獲取查詢字符串的方法是:

app.get(/api/videos/:id, (req, res) => {
  res.send(req.query) // 獲取查詢字符串
})

處理post請(qǐng)求

前面我們講了處理HTTP的get請(qǐng)求,我們用get處理了獲取所有數(shù)據(jù)和單一數(shù)據(jù)的請(qǐng)求,現(xiàn)在讓我們來看看如何處理post請(qǐng)求,我們使用post請(qǐng)求來創(chuàng)建新數(shù)據(jù)。具體步驟如下:

  1. 首先需要讀取request的請(qǐng)求體的數(shù)據(jù),用請(qǐng)求體的數(shù)據(jù)創(chuàng)建一條數(shù)據(jù);
  2. 然后添加到所有數(shù)據(jù)中。

要讀取請(qǐng)求體中的數(shù)據(jù),需要打開Express獲取請(qǐng)求體中JSON對(duì)象的功能(這個(gè)功能默認(rèn)是關(guān)閉的)

主要代碼如下:

app.use(express.json()) // 開啟Express讀取請(qǐng)求體JSON數(shù)據(jù)功能

// 處理post請(qǐng)求
app.post('/api/videos', (req, res) => {
let video = {
id: videos.length + 1, // 因?yàn)闆]有用數(shù)據(jù)庫,所以手工設(shè)置id
name: req.body.name
}
videos.push(video)
// 最后,按照慣例,我們應(yīng)該返回新創(chuàng)建的數(shù)據(jù),有可能客戶端需要用到它
res.send(video)
})

app.use(express.json())看起來有點(diǎn)陌生,沒關(guān)系,我們會(huì)在后面的中間件章節(jié)中講解,當(dāng)我們調(diào)用express.json()這個(gè)方法時(shí),返回一個(gè)中間件,然后使用app.use方法在處理請(qǐng)求流程中使用這個(gè)中間件。編寫完成后,我們可以在Chrome中添加Postman這個(gè)插件來請(qǐng)求我們的post接口:

數(shù)據(jù)驗(yàn)證

從安全角度考慮,永遠(yuǎn)不要相信客戶端發(fā)給你的東西,記住永遠(yuǎn)要驗(yàn)證輸入的內(nèi)容,所以上面的post請(qǐng)求,我們應(yīng)該增加驗(yàn)證:

// 驗(yàn)證輸入
// 處理post請(qǐng)求
app.post('/api/videos', (req, res) => {
// 驗(yàn)證輸入
if (!req.body.name || req.body.name.length < 3) {
// 返回:400 狀態(tài)碼,表示 Bad Request,是一個(gè)錯(cuò)誤的請(qǐng)求
res.status(400).send('Name is required and should be minimum 3 characaters')
return
}
let video = {
id: videos.length + 1, // 因?yàn)闆]有用數(shù)據(jù)庫,所以手工設(shè)置id
name: req.body.name
}
videos.push(video)
// 最后,按照慣例,我們應(yīng)該返回新創(chuàng)建的數(shù)據(jù),有可能客戶端需要用到它
res.send(video)
})

在現(xiàn)實(shí)開發(fā)中,面對(duì)復(fù)雜的應(yīng)用,很可能代碼比這個(gè)復(fù)雜的多,如果你不想在最開始的時(shí)候就手寫這么復(fù)雜輸入驗(yàn)證邏輯,有一個(gè)可以輕松驗(yàn)證輸入的包:joi,讓我們看看如何使用joi

npm i joi

在使用Joi之前,要先定義一個(gè)schema(模式),schema定義了對(duì)象外觀的特征,比如對(duì)象中應(yīng)該有什么屬性,屬性的類型是什么,最小和最大的字符數(shù)是多少,有沒有包含數(shù)字,數(shù)字的范圍是什么。。。等等,具體操作看下面代碼:

const Joi = require('joi') // 引入joi庫
// 處理post請(qǐng)求
app.post('/api/videos', (req, res) => {
// 使用joi 驗(yàn)證輸入
// 1. 定義一個(gè)schema
const schema = {
// 表示:有一個(gè)name屬性,類型為字符串,最小長(zhǎng)度為3,是必須的
name: Joi.string().min(3).required()
}
// 2.把請(qǐng)求數(shù)據(jù)和 schema 傳遞給validate方法中驗(yàn)證,返回一個(gè)對(duì)象
const result = Joi.validate(req.body, schema)

// 查看結(jié)果,在發(fā)起post請(qǐng)求時(shí),試試合法和不合法的參數(shù),然后查看result是什么
// 如果合法,error 為null,不合法,error的值是具體的錯(cuò)誤信息
console.log(result)
// { error: null,
// value: { name: '123' },
// then: [Function: then],
// catch: [Function: catch] }

// 3.判斷是否有合法(error不為null表示數(shù)據(jù)有誤),那么就返回400和錯(cuò)誤信息
if (result.error) return res.status(400).send(result.error.details[0].message)

// 添加數(shù)據(jù)
let video = {
id: videos.length + 1, // 因?yàn)闆]有用數(shù)據(jù)庫,所以手工設(shè)置id
name: req.body.name
}
videos.push(video)
// 最后,按照慣例,我們應(yīng)該返回新創(chuàng)建的數(shù)據(jù),有可能客戶端需要用到它
res.send(video)
})

小結(jié)
對(duì)于客戶端發(fā)過來的數(shù)據(jù),一定要進(jìn)行驗(yàn)證,使用joi庫可以簡(jiǎn)單且直觀的進(jìn)行驗(yàn)證,joi還提供了很多其它驗(yàn)證功能,具體的可以在使用到的時(shí)候查看官方GitHub。

處理put請(qǐng)求

要對(duì)已有數(shù)據(jù)進(jìn)行更改,應(yīng)當(dāng)使用put請(qǐng)求進(jìn)行更新數(shù)據(jù),下面讓我們看看如何使用put請(qǐng)求更新數(shù)據(jù):

app.put('/api/videos/:id', (req, res) => {
// 1. 找到指定id的電影,如果存在,返回404
let video = videos.find(v => v.id === parseInt(req.params.id))
if (!video) return res.status(404).send('The video is not found')

// 2. 驗(yàn)證傳遞過來的電影對(duì)象(數(shù)據(jù)),如果不合法,返回400
const schema = {
name: Joi.string().min(3).required()
}
const { error }= Joi.validate(req.body, schema)

if (error) return res.status(400).send(error.details[0].message)

// 3.更新電影數(shù)據(jù),返回更新后的電影數(shù)據(jù)
video.name = req.body.name
res.send(video)
})

處理delete請(qǐng)求

到目前為止,我們實(shí)現(xiàn)了增、查和改操作,接下來我們看看刪除操作,它跟之前的操作非常相似,具體代碼如下:

// 刪除操作
app.delete('/api/videos/:id', (req, res) => {
// 1. 根據(jù)參數(shù) id 查找指定的電影,如果不存在返回404
let video = videos.find(v => v.id === parseInt(req.params.id))
if (!video) return res.status(404).send('The video is not found')

// 2.刪除指定的電影
let index = videos.indexOf(video)
videos.splice(index, 1)

// 3. 返回刪除的電影
res.send(video)
})

總結(jié)

在本章節(jié)中,我們主要講解了什么是RESTful API,并且使用 Express 處理 CRUD 請(qǐng)求,后面我們還會(huì)涉及數(shù)據(jù)庫的操作,會(huì)越來越全面哦!感謝閱讀,希望本篇文章能讓你收獲知識(shí)O(∩_∩)O~~

本文章轉(zhuǎn)載微信公眾號(hào)@Node開發(fā)技術(shù)

上一篇:

ASP.NET Web API快速入門介紹

下一篇:

如何用 OpenAPI 在 Express 中構(gòu)建更好的 API
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊(cè)

多API并行試用

數(shù)據(jù)驅(qū)動(dòng)選型,提升決策效率

查看全部API→
??

熱門場(chǎng)景實(shí)測(cè),選對(duì)API

#AI文本生成大模型API

對(duì)比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)

#AI深度推理大模型API

對(duì)比大模型API的邏輯推理準(zhǔn)確性、分析深度、可視化建議合理性

10個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)