從0搭建express應用結構
- 一、express基礎
- 1、express簡介
- 2、express安裝
- 二、中間件
- 1、什么是中間件
- 2、撰寫普通中間件
- 3、撰寫path匹配中間件
- 4、撰寫methods匹配中間件
- 四、資料決議
- 1、url決議
- 2、json格式資料決議
- 3、application/x-www-form-urlencoded資料決議
- 4、from-data資料決議
- 5、檔案上傳
- 五、express路由
- 六、靜態資源服務器
- 七、利用 morgan庫保存request日志
一、express基礎
1、express簡介
- Express 是一個保持最小規模的靈活的 Node.js Web 應用程式開發框架,為 Web 和移動應用程式提供一組強大的功能,
- Express 提供精簡的基本 Web 應用程式功能,而不會隱藏 Node.js的功能,
- 許多流行的開發框架都基于Express 構建
2、express安裝
假設你已經安裝過了node.js,你需要為你的express應用創建一個檔案夾為接下來的操作準備,
-
npm init為express應用創建一個package.json檔案,需要輸入引數直接回車默認即可,json檔案中會告訴你express依賴的其他包,

-
接下來在你新建的目錄下安裝 Express
npm install express --save并將其保存到依賴串列中,或者臨時安裝npm install express --no-save

二、中間件
1、什么是中間件
Express是一個路由和中間件的Web框架,它本身的功能非常少,Express的核心就是中間件,Express應用程式本質上可以理解為是一系列中間件函式的呼叫,

中間件的本質是傳遞給express的一個回呼函式,這個回呼函式接受三個引數
- ? 請求物件(request)
- ? 回應物件(response)
- ? next函式(用于執行下一個中間件的函式)
中間件可以干嘛呢?
- 🎉 執行任何代碼
- 🎉 更改請求和回應物件
- 🎉 結束請求、回應周期(回傳資料)
- 🎉 呼叫下一個中間件
如果匹配的第一個中間件沒有結束請求-回應周期(沒有呼叫res.end),則必須呼叫next將控制權傳遞給下一個中間件,不然請求將一直處于掛起狀態
執行下列代碼后在瀏覽中打開localhost:8089,服務器會列印出express體驗成功,但是瀏覽器會一直處于加載中的狀態
const express = require('express')
const app = express()
app.get('/', () => {
})
app.listen(8089, () => {
console.log('express體驗成功')
})
2、撰寫普通中間件
express主要提供了兩種方式撰寫中間件,本文選用app這種方式
- 🎄 app.use
- 🎄 router.use
這里用postman模擬客戶端給服務器發送請求,對postman還不是很了解的可以看這篇文章,里面有簡單描述postman的基礎用法👉👉👉node.js+postman簡單模擬HTTP服務器與客戶端互動

服務器處理請求
const express = require('express')
// express本質是一個函式:createApplication
// 呼叫這個函式回傳一個app,app本質上也是一個函式
const app = express()
// app.use注冊的中間件(回呼函式)可以被任意的http請求執行
app.use((req, res, next) => {
console.log('中間件1')
// res.end只是表示結束請求-回應的周期,并不影響next執行下一個中間件
res.end('welcome back')
// 執行完本中間件后繼續查找執行能匹配的中間件
next()
})
// 客戶端發送網路請求之后,每個中間件都能匹配到請求,但是只有第一個會回應
//如果沒有next指向下一個中間件的話就只會執行第一個匹配的中間件
app.use((req, res, next) => {
console.log('中間件2')
})
app.use((req, res, next) => {
console.log('中間件3')
})
// 開啟監聽
app.listen(8089, () => {
console.log('express體驗成功')
})
//express體驗成功
//中間件1
//中間件2
3、撰寫path匹配中間件
在postman上模擬給服務器發送請求

服務器處理客戶端(postman)發送的請求
const express = require('express')
const app = express()
//'/register'是介面用來判斷中間件是否匹配
app.use('/register', (req, res, next) => {
console.log('我是1')
res.end('hollow路徑匹配中間件1')
})
//只有和客戶端發送請求時回傳的介面一致中間件才會匹配執行
app.use('/login', (req, res, next) => {
console.log('我是2')
res.end('hollow路徑匹配中間件2')
})
// 多個中間件只會執行匹配的第一個,除非有next會按next順序執行
app.use('/login', (req, res, next) => {
console.log('我是3')
res.end('hollow路徑匹配中間件3')
})
app.listen(8089, () => {
console.log('express體驗成功')
})
//express體驗成功
//我是2
4、撰寫methods匹配中間件
methods匹配中間件一樣也主要有兩種
- 🎄 app.methods
- 🎄 router.methods
其中methods指的是常用的http請求的方式,例如get或者post等
在postman上給localhost:8089/login發送get請求

服務器根據請求的路徑和方法匹配適合的中間件
const express = require('express')
const app = express()
// 即使請求的路徑和方法都與下面的app.get匹配,也會只執行第一個中間件(app.use可以被任何的http請求執行),除非有next指向下一個
// app.use((req, res, next) => {
// console.log('普通中間件')
// next()
// })
// 只有路徑為/login,請求方法為get才能匹配成功
app.get('/login', (req, res, next) => {
console.log('get方法')
res.end('hollow get')
})
app.post('/login', (req, res, next) => {
console.log('post方法')
res.end('hollow post')
})
app.listen(8089, () => {
console.log('express體驗成功')
})
//express體驗成功
//get方法
四、資料決議
- 客戶端發送get請求時一般會將資料放到url中傳遞給服務器,所以get請求傳遞的資料安全性和大小都有一定限制
- 客戶端發送post請求時會將資料放到body中,客戶端可以通過json的方式傳遞也可以通過application/x-www-form-urlencoded格式或者from-data格式傳遞
1、url決議
postman模擬客戶端將資料通過url發送給服務器

服務器根據客戶端的介面對url中的資訊進行處理
const express = require('express')
const app = express()
// 相當于動態路由,后面的:id是根據客戶端動態傳過來的
// localhost:8089/products/apple/12
app.get("/products/:id/:neme", (req, res, next) => {
// 真實場景下可以通過url獲取介面/products/后面的id,然后可以通過這個id去資料庫里面查詢商品資料,再通過res回傳給客戶端
//params方法可以對傳遞過來的url進行處理
console.log(req.params)
res.end("商品詳情資料")
})
app.listen('8089', (req, res, next) => {
console.log("服務器開啟成功")
})
//服務器開啟成功
//{ id: 'apple', neme: '12' }
postman中可以直接預覽query決議url后的引數

服務端對url中的資料進行決議
const express = require('express')
const app = express()
app.get("/login", (req, res, next) => {
console.log(req.query)
res.end("用戶登錄成功")
})
app.listen('8089', (req, res, next) => {
console.log("服務器開啟成功")
})
// 服務器開啟成功
//{ username: '阿花', password: 'ahua' }
2、json格式資料決議
postman以json格式給服務器傳遞資料

服務器對客戶端的json資料進行決議
const express = require('express')
const app = express()
// 自己撰寫的node原生json決議
// app.use((req, res, next) => {
// if (req.headers["content-type"] === 'application/json') {
// // 提前對資料進行決議,然后傳遞給后面的中間件,免得每次呼叫中間件都要決議一次資料
// req.on('data', data => {
// // console.log(data.toString())
// const info = JSON.parse(data.toString())
// // console.log(info)
// // 相當于給req物件身上加了一個屬性叫body
// req.body = info
// })
// req.on('end', () => {
// next()
// })
// } else {
// // next中不能傳遞引數,有錯誤的時候才會在next中傳遞引數
// next()
// }
// })
// express中的json方法決議
// json格式決議,決議后的結果放在了res.body屬性中
app.use(express.json())
app.post('/login', (req, res, next) => {
console.log(req.body)
res.end('welcome to home~')
})
app.listen(8089, () => {
console.log('express體驗成功')
})
//express體驗成功
//{ username: '阿花', password: 'ahua' }
3、application/x-www-form-urlencoded資料決議
在postman中選中application/x-www-form-urlencoded格式,填寫要給服務器傳遞的資料

服務器進行決議
const express = require('express')
const app = express()
// x-www-from-urlencoded格式決議
// false表明用node的內置模塊querystring來決議
// true表示使用第三方庫qs來決議
// urlencoded格式決議,決議后的結果放在了res.body屬性中
app.use(express.urlencoded({ extended: true }))
app.post('/produces', (req, res, next) => {
console.log(req.body)
res.end('upload product info success~')
})
app.listen(8089, () => {
console.log('express體驗成功')
})
4、from-data資料決議
express中沒有直接對from-data格式資料進行決議的功能,但是可以利用官方庫multer,可以去GitHub搜multer了解詳細的資訊,安裝完成后便能實作對from-data資料的決議

用postman給服務器發送form-data格式資料

服務器決議
const express = require('express')
//匯入multer
const multer = require('multer')
const app = express()
// 呼叫multer函式本質上是創建了一個物件
const upload = multer()
// 處理from-data型別一個只有文本域的表單,使用 .none():
app.use(upload.none())
app.post('/login', (req, res, next) => {
console.log(req.body)
res.end('用戶登錄成功')
})
app.listen(8089, () => {
console.log('from-data決議服務器')
})
//from-data決議服務器
//[Object: null prototype] { name: '阿花', id: 'ahua' }
5、檔案上傳
用postman給服務上傳檔案,這里我選擇的是一張圖片

服務器通過官方庫multer對上傳的檔案進行決議
const path = require('path')
const express = require('express')
const multer = require('multer')
const app = express()
const storage = multer.diskStorage({
// 存盤位置
destination: (req, fill, callback) => {
// 第一個引數表示有沒有錯誤,第二個是路徑
//如果沒有這個檔案夾,express會默認創建一個
callback(null, './uploads/')
},
// 檔案名
filename: (req, fill, callback) => {
// 第一個引數表示有沒有錯誤,第二個是檔案名
//這樣定義檔案名可以防止客戶端傳遞過來多個同名檔案時發生的覆寫
// fill.originalname原始檔案名
callback(null, Date.now() + path.extname(fill.originalname))
}
})
const upload = multer({
// 在multer中宣告上傳后檔案存盤的位置
//不自定義storage時服務器拿到的只是一個二進制檔案
// dest: './uploads'
// 自定義存盤資訊,包括檔案名,位置等
storage
})
//.any()可以接受一切上傳的檔案,檔案陣列將保存在 req.files,
// '/upload'是介面用來判斷是否需要做檔案上傳
// upload.single(file)檔案上傳的中間件,從客戶端獲取到檔案然后存放在指定位置,file是上傳檔案的key值,單個檔案用single,多個檔案用array
app.post('/upload', upload.single('file'), (req, res, next) => {
//單個保存在req.file中
console.log(req.file)
res.end('上傳成功')
})
//多種檔案用.any(),可以接受一切上傳的檔案,檔案陣列將保存在 req.files,
// 但是不能將upload.any()作為全域中間件(app.use(upload.any()))
// 因為用戶會將資料傳到一個預料不到的路由,所以應該只在需要處理檔案的路由上使用
app.listen(8089, () => {
console.log('from-data檔案上傳')
})
在代碼中將storage注釋掉,打開 dest: './uploads',服務器決議到的會是標注的二進制檔案

五、express路由
如果我們將所有的代碼邏輯都寫在app中,那么app會變得越來越復雜,一方面完整的Web服務器包含非常多的處理邏輯,另一方面有些處理邏輯其實是一個整體,我們應該將它們放在一起
比如對users相關的處理
- 獲取用戶串列;
- 獲取某一個用戶資訊;
- 創建一個新的用戶;
所以最好還是將他們放在單獨的檔案中,再參考進來即可
//user.js檔案
const express = require('express')
const router = express.Router()
router.get('/', (req, res, next) => {
res.json(["xiaoming", "ahua", "tingting"])
res.end("獲取用戶串列")
})
router.get('/:id', (req, res, next) => {
res.json(`${req.params.id}用戶資訊`)
})
router.post('/', (req, res, next) => {
res.json("create user success~")
})
module.exports = router
//主檔案
const express = require('express')
//匯入user的路由
const UserRouter = require('./router/users')
const app = express()
app.use('/user', UserRouter)
app.listen(8089, (req, res, next) => {
console.log("路由服務器啟動成功~")
})
檔案目錄

六、靜態資源服務器
部署靜態資源我們可以選擇很多方式,node也可以作為靜態資源服務器,并且express提供了很方便的部署方式,這里我放的是之前寫的一個css的3d旋轉相冊,大家可以放自己的一些代碼,

const express = require('express')
const app = express()
// 使用node原生的http方法需要去讀取檔案夾里面的檔案然后再給客戶端回傳
// express里面使用中間件會自動找到檔案夾把他作為靜態服務器對應的檔案夾
// 當請求靜態資源的時候就會去這個檔案夾里面找
app.use(express.static('./css-3d-imags/'))
app.listen('8089', () => {
console.log("靜態服務器開啟成功")
})
瀏覽器效果展示

七、利用 morgan庫保存request日志
morgan也是express官方的庫

const express = require('express')
const fs = require("fs")
const morgan = require("morgan")
const app = express()
//寫入的位置,每次有新內容都會進行追加
const writeStream = fs.createWriteStream('./log/access.log', {
flags: "a+"
})
// combined用于決定列印的日志的保存格式
// stream: writeStream表示一旦有日志就會把日志寫入到相應的某個位置
// 列印所有的日志可以用app.use()的全域中間件,也就是下面這種用法
// 只關注某一個介面的日志的話直接把中間件放到想關注的介面的中間的位置即可
app.use(morgan("combined", { stream: writeStream }))
app.get('/log', (req, res, next) => {
res.end("write log")
})
app.listen('8089', (req, res, next) => {
console.log('服務器啟動成功')
})
目錄結構

log中記錄了每次發送請求的時間,請求方式,狀態碼以及請求來源

從零搭建express應用實作簡單Web服務器功能基本就到這里了,如果有什么不妥當的歡迎指正🙋?♂?🙋?♀?
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/291507.html
標籤:其他
上一篇:JavaScript入門第八章(流程控制與分支結構)
下一篇:jQuery的選擇器
