主頁 > 前端設計 > 基于Node.js搭建express應用實作簡單Web服務器功能,建議收藏

基于Node.js搭建express應用實作簡單Web服務器功能,建議收藏

2021-08-03 07:49:49 前端設計

從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簡介

  1. Express 是一個保持最小規模的靈活的 Node.js Web 應用程式開發框架,為 Web 和移動應用程式提供一組強大的功能,
  2. Express 提供精簡的基本 Web 應用程式功能,而不會隱藏 Node.js的功能,
  3. 許多流行的開發框架都基于Express 構建

2、express安裝

假設你已經安裝過了node.js,你需要為你的express應用創建一個檔案夾為接下來的操作準備,

  1. npm init為express應用創建一個package.json檔案,需要輸入引數直接回車默認即可,json檔案中會告訴你express依賴的其他包,
    在這里插入圖片描述

  2. 接下來在你新建的目錄下安裝 Expressnpm 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后的引數
postman模擬客戶端將資料通過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的選擇器

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • vue移動端上拉加載

    可能做得過于簡單或者比較low,請各位大佬留情,一起探討技術 ......

    uj5u.com 2020-09-10 04:38:07 more
  • 優美網站首頁,頂部多層導航

    一個個人用的瀏覽器首頁,可以把一下常用的網站放在這里,平常打開會比較方便。 第一步,HTML代碼 <script src=https://www.cnblogs.com/szharf/p/"js/jquery-3.4.1.min.js"></script> <div id="navigate"> <ul> <li class="labels labels_1"> ......

    uj5u.com 2020-09-10 04:38:47 more
  • 頁面為要加<!DOCTYPE html>

    最近因為寫一個js函式,需要用到$(window).height(); 由于手寫demo的時候,過于自信,其實對前端方面的認識也不夠體系,用文本檔案直接敲出來的html代碼,第一行沒有加上<!DOCTYPE html> 導致了$(window).height();的結果直接是整個document的高 ......

    uj5u.com 2020-09-10 04:38:52 more
  • WordPress網站程式手動升級要做好資料備份

    WordPress博客網站程式在進行升級前,必須要做好網站資料的備份,這個問題良家佐言是遇見過的;在剛開始接觸WordPress博客程式的時候,因為升級問題和博客網站的修改的一些嘗試,良家佐言是吃盡了苦頭。因為購買的是西部數碼的空間和域名,每當佐言把自己的WordPress博客網站搞到一塌糊涂的時候 ......

    uj5u.com 2020-09-10 04:39:30 more
  • WordPress程式不能升級為5.4.2版本的原因

    WordPress是一款個人博客系統,受到英文博客愛好者和中文博客愛好者的追捧,并逐步演化成一款內容管理系統軟體;它是使用PHP語言和MySQL資料庫開發的,用戶可以在支持PHP和MySQL資料庫的服務器上使用自己的博客。每一次WordPress程式的更新,就會牽動無數WordPress愛好者的心, ......

    uj5u.com 2020-09-10 04:39:49 more
  • 使用CSS3的偽元素進行首字母下沉和首行改變樣式

    網頁中常見的一種效果,首字改變樣式或者首行改變樣式,效果如下圖。 代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, ......

    uj5u.com 2020-09-10 04:40:09 more
  • 關于a標簽的講解

    什么是a標簽? <a> 標簽定義超鏈接,用于從一個頁面鏈接到另一個頁面。 <a> 元素最重要的屬性是 href 屬性,它指定鏈接的目標。 a標簽的語法格式:<a href=https://www.cnblogs.com/summerxbc/p/"指定要跳轉的目標界面的鏈接">需要展示給用戶看見的內容</a> a標簽 在所有瀏覽器中,鏈接的默認外觀如下: 未被訪問的鏈接帶 ......

    uj5u.com 2020-09-10 04:40:11 more
  • 前端輪播圖

    在需要輪播的頁面是引入swiper.min.js和swiper.min.css swiper.min.js地址: 鏈接:https://pan.baidu.com/s/15Uh516YHa4CV3X-RyjEIWw 提取碼:4aks swiper.min.css地址 鏈接:https://pan.b ......

    uj5u.com 2020-09-10 04:40:13 more
  • 如何設定html中的背景圖片(全屏顯示,且不拉伸)

    1 <style>2 body{background-image:url(https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture); 3 background-size:cover;background ......

    uj5u.com 2020-09-10 04:40:16 more
  • Java學習——HTML詳解(上)

    HTML詳解 初識HTML Hyper Text Markup Language(超文本標記語言) 1 <!--DOCTYPE:告訴瀏覽器我們要使用什么規范--> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <!--meta 描述性的標簽,描述一些 ......

    uj5u.com 2020-09-10 04:40:33 more
最新发布
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 07:59:23 more
  • 生產事故-走近科學之消失的JWT

    入職多年,面對生產環境,盡管都是小心翼翼,慎之又慎,還是難免捅出簍子。輕則滿頭大汗,面紅耳赤。重則系統停擺,損失資金。每一個生產事故的背后,都是寶貴的經驗和教訓,都是專案成員的血淚史。為了更好地防范和遏制今后的各類事故,特開此專題,長期更新和記錄大大小小的各類事故。有些是親身經歷,有些是經人耳傳口授 ......

    uj5u.com 2023-04-18 07:55:04 more
  • 記錄--Canvas實作打飛字游戲

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。螢屏上,有一個白色的矩形框,里面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與螢屏上的單詞匹配,那么我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那么我就會輸 ......

    uj5u.com 2023-04-04 08:35:30 more
  • 了解 HTTP 看這一篇就夠

    在學習網路之前,了解它的歷史能夠幫助我們明白為何它會發展為如今這個樣子,引發探究網路的興趣。下面的這張圖片就展示了“互聯網”誕生至今的發展歷程。 ......

    uj5u.com 2023-03-16 11:00:15 more
  • 藍牙-低功耗中心設備

    //11.開啟藍牙配接器 openBluetoothAdapter //21.開始搜索藍牙設備 startBluetoothDevicesDiscovery //31.開啟監聽搜索藍牙設備 onBluetoothDeviceFound //30.停止監聽搜索藍牙設備 offBluetoothDevi ......

    uj5u.com 2023-03-15 09:06:45 more
  • canvas畫板(滑鼠和觸摸)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canves</title> <style> #canvas { cursor:url(../images/pen.png),crosshair; } #canvasdiv{ bo ......

    uj5u.com 2023-02-15 08:56:31 more
  • 手機端H5 實作自定義拍照界面

    手機端 H5 實作自定義拍照界面也可以使用 MediaDevices API 和 <video> 標簽來實作,和在桌面端做法基本一致。 首先,使用 MediaDevices.getUserMedia() 方法獲取攝像頭媒體流,并將其傳遞給 <video> 標簽進行渲染。 接著,使用 HTML 的 < ......

    uj5u.com 2023-01-12 07:58:22 more
  • 記錄--短視頻滑動播放在 H5 下的實作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 短視頻已經無數不在了,但是主體還是使用 app 來承載的。本文講述 H5 如何實作 app 的視頻滑動體驗。 無聲勝有聲,一圖頂百辯,且看下圖: 網址鏈接(需在微信或者手Q中瀏覽) 從上圖可以看到,我們主要實作的功能也是本文要講解的有: ......

    uj5u.com 2023-01-04 07:29:05 more
  • 一文讀懂 HTTP/1 HTTP/2 HTTP/3

    從 1989 年萬維網(www)誕生,HTTP(HyperText Transfer Protocol)經歷了眾多版本迭代,WebSocket 也在期間萌芽。1991 年 HTTP0.9 被發明。1996 年出現了 HTTP1.0。2015 年 HTTP2 正式發布。2020 年 HTTP3 或能正... ......

    uj5u.com 2022-12-24 06:56:02 more
  • 【HTML基礎篇002】HTML之form表單超詳解

    ??一、form表單是什么

    ??二、form表單的屬性

    ??三、input中的各種Type屬性值

    ??四、標簽 ......

    uj5u.com 2022-12-18 07:17:06 more