nodejs(八)express使用詳解

express使用教程

[TOC]html

1.1 介紹

Express 是一個基於 Node.js 封裝的上層服務框架,它提供了更簡潔的 API 更實用的新功能。它經過中間件和路由讓程序的組織管理變的更加容易;它提供了豐富的 HTTP 工具;它讓動態視圖的渲染變的更加容易;它還定義了一組可拓展標準。前端

1.2 安裝

npm install express -S
複製代碼

1.3 hello world

let express = require('express')
let app = express()

app.get('/',(req,res) => res.end('hello world'))

app.listen(3000,() => console.log('Server is running...'))
複製代碼

運行示例node

二. express中的靜態服務

在web網站後端開發的過程當中,咱們每每須要把一些靜態文件夾暴露出去,用戶能夠根據url地址去訪問到其中的內容,這些靜態文件每每也稱之爲公共資源,利用express框架能夠方便地託管靜態文件。git

2.1 託管靜態文件

  1. 在項目目錄新建 public 靜態資源的文件夾
  2. 託管靜態文件:託管靜態文件的方法有三種

2.2 託管方法

方法一推薦github

app.use('/public/',express.static('./public'))
複製代碼

app.use() 方法傳遞兩個參數,第一個參數爲訪問的URL前綴,第二個爲要託管的目錄即要暴露的文件目錄web

let express = require('express');
let app = express()

//設置靜態資源
// 方法一
app.use('/public/',express.static('./public'))

app.listen(3000,() => console.log('Server is running...'))
複製代碼

示例shell

方法二數據庫

app.use(express.static('./public'))
複製代碼

方法二在方法一的基礎上,省略第一個參數,直接指定暴露的靜態資源文件夾express

let express = require('express');
let app = express()

//設置靜態資源
// 方法二
app.use(express.static('./public'))

app.listen(3000,() => console.log('Server is running...'))
複製代碼

示例npm

方法三

app.use('/static/',express.static('./public'))
複製代碼

同方法一傳參相似,只不過將第一個參數設置爲你想訪問的URL前綴,即指定前綴的別名

let express = require('express');
let app = express()

//設置靜態資源
// 方法三
app.use('/static/',express.static('./public'))

app.listen(3000,() => console.log('Server is running...'))
複製代碼

示例

三. express中獲取post請求數據

在post請求中,請求的參數包含在 請求的body中,咱們要獲取post請求的數據,便是要獲取request.body裏面的數據

可是,在默認狀況下,咱們使用 req.body獲取到的內容爲undefine,這裏須要使用一箇中間件body-parser來解析傳入的請求主體,使req.body能夠被訪問

1. 安裝 body-parser

npm install body-parser express -S
複製代碼

2. 建立server.js

// server.js

//引入模塊
const express = require('express')
const bodyParse = require('body-parser')
// 建立服務器對象
const app = express()

/** * 經過 body-parser中間件解析req.body * 根據不一樣的 Content-Type分別有以下兩種不一樣的配置 * post請求體中的Content—Type爲:application/x-www-form-urlencoded,使用配置1 * post請求體中的Content-Type爲:application/json,使用配置2 */
app.use(bodyParse.urlencoded({extended:false}))
app.use(bodyParse.json())

//配置訪問路由,返回post請求中的name參數
app.post('/post',(req,res)=>{
    const result = req.body
    console.log(result)
    res.end(result.name)
})

app.listen(3000,()=>console.log('Server is running on localhost:3000'))
複製代碼

3. 開啓服務器,可經過postman訪問URL,自行配置body裏面的參數信息,以及指定Content-Type

4. 最後點擊左下角的body,便可查看咱們服務器中的返回信息

四. express中 art-template的使用

前面咱們學習瞭如何在Nodejs中使用art-template,在express一樣可使用,接下來咱們就來看看,如何在express中使用art-template

1. 在express中使用art-template,首先要安裝以下兩個模塊

npm install art-template express-art-template -S
複製代碼

2. 添加模版界面

// index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>{{ title }}</title>
</head>
<body>
    <h1>{{ title }}</h1>
    <p>My name is {{ name }}</p>
    <p>I come from {{ from }}</p>
    <p>I like {{each hobbies}} {{ $value }} {{ /each }}</p>
</body>
</html>
複製代碼

3. 添加server.js文件

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


// 配置 express-art-template模版引擎,
//配置以後,服務器請求事件的回調函數的響應對象上會增長一個 render方法,用於渲染模版字符串返回渲染以後的結果
// 第一個參數 html 爲模版文件拓展名,表示模版文件必須是HTML文件
app.engine('html',require('express-art-template'))

app.get('/',(req,res)=>{
    res.render('./index.html',{
        title: '我的介紹',
        name: 'Kobe',
        form: 'America',
        bobbies: ['basketball','swimming']
    })
})

app.listen(8000,()=>console.log('Server is running on localhost:3000'))
複製代碼

4. 啓動服務器,調用URL,咱們發現,報以下錯誤

Error: Failed to lookup view "./index.html" in views directory "C:\Users\admin\Desktop\work\ForStudy\8.Node.js\DemoFolder\express-art-template-demo\views"
複製代碼

錯誤分析

  • 調用res.render方法的時候,默認會去同級目錄的 views文件夾下面去找對應的文件
  • 在這裏因爲咱們的server.js和index.html在同級目錄下,因而系統找不到views目錄下的index.html文件,因此報錯
  • **規範:**咱們通常將模版文件放在與server.js同級的views文件夾下面,方便調用
  • **自定義:**咱們還能夠經過以下方法,自定義咱們但願查找模版界面的目錄
app.set('views', '替換的文件路徑') // 第一個參數必須爲:views
複製代碼

五. express中路由模塊的提取

路由分爲前端路由和後端路由,這裏咱們主要談論的過後端路由,即URL地址

5.1 路由的概念

後端路由

對於普通網站,全部的連接都是URL地址,URL地址即對應服務器上面的資源

前端路由

前端路由主要經過hash(#)來實現,經過hash值的改變來切換界面,同同時,hash值的改變並不會帶來http請求的從新響應,使得加載頁面的性能更好

5.2 路由的提取

經過前面的學習咱們知道,能夠經過express的get和post方法,對不一樣的URL地址作出響應,最終將結果返回給客戶端

**問題描述:**一旦咱們的項目界面多了起來,須要處理的請求路徑就會比較多,若是所有寫在一個文件,就會使得文件難以維護,因而咱們能夠提取路由文件,專門用來配置路由,最後把路由對象導出掛載到服務器上便可

實現:

  1. 使用 npm init -y 初始化,而且安裝 express 模塊

    npm init -y
    npm install express -S
    複製代碼
  2. 建立server.js文件

    // server.js
    
    const express = require('express')
    const server = express()
    
    server.listen(3000,()=>{
        console.log('Server is running on localhost:3000')
    })
    複製代碼
  3. 路由文件

    // router.js
    
    // 引入express而且獲取路由對象
    const app = require('express')
    const router = app.Router()
    
    // 配置路由對象
    router.get('/',(req,res)=>{
        res.send('主頁')
    })
    
    router.get('/login',(req,res)=>{
        res.send('登錄')
    })
    
    router.get('/register',(req,res)=>{
        res.send('註冊')
    })
    
    // 導出路由對象
    module.exports = router
    複製代碼
  4. 在server.js文件中掛載路由對象

    // server.js
    
    const express = require('express')
    const server = express()
    //掛載路由對象
    const router = require('./router')
    server.use(router)
    
    server.listen(3000,()=>{
        console.log('Server is running on localhost:3000')
    })
    複製代碼
  5. 演示

六. express框架案例-CRUD

在這裏,咱們一塊兒經過express框架,來實現一個擁有增刪改查功能的簡易版學生管理系統

一共有以下幾個文件

  • db.json 存儲數據的文件,模擬數據庫
  • StudentApi.js API實現文件
  • test.js 測試文件
// db.json

{
    "students": [
        {
            "id": 1,
            "name": "YaoMing",
            "sex": "M",
            "hobbies": [
                "Basketball",
                "ComputerGame",
                "eating"
            ]
        },
        {
            "id": 2,
            "name": "YiJianLian",
            "sex": "M",
            "hobbies": [
                "Basketball",
                "earnMoney",
                "coding"
            ]
        },
        {
            "id": 3,
            "name": "Kobe",
            "sex": "M",
            "hobbies": [
                "basketball",
                "swimming",
                "sleep"
            ]
        }
    ]
}
複製代碼
// StudentApi.js

/** * 提供一個簡單的CRUD接口 * 可經過以下增刪改查的接口,實現一個簡易的學生管理系統 * 同時,可做爲nodejs增刪改查的模板 */


const fs = require('fs')
// 提取數據源地址,方便後續維護
const dataBase = './db.json'

/** * 新增學生信息 * Param:要新增的學生對象 * 調用:add(student) * */
exports.add = (student)=>{
    fs.readFile(dataBase,(err,data)=>{
        // 錯誤處理
        if(err){
            return console.log('readFiel db.json error...')
        }
        // 獲取學生數組
        var students = JSON.parse(data).students
        // 爲新增的學生對象設置id值
        if(students.length === 0){
            student.id = 1
        }
        student.id = students[students.length-1].id + 1
        // 將新增的學生對象添加到學生數組
        students.push(student)
        let dataStr = JSON.stringify({studnets:studnets})
        // 將新增完成的學生數組寫入文件
        fs.writeFile(dataBase,dataStr,(err)=>{
            if(err){
                return console.log('write to db.json failed...')
            }
            return console.log('添加成功')
        })
    })
}

/** * 更新學生信息 * Param: 要修改的學生對象 */
exports.update = (student)=>{
    fs.readFile(dataBase,(err,data)=>{
        // 錯誤處理
        if(err){
            return console.log('readFiel db.json error...')
        }

        // 獲取學生數組
        var students = JSON.parse(data).students
        // 更新標識,用來判斷是否有數據被更新
        var isUpdate = false

        // 遍歷學生數組,找到要更新的那一個學生信息
        for(var i = 0;i<students.length;i++){
            // 當學生信息都沒有發生變化的時候,不作更新
            if(students[i].id === student.id){
                if(students[i].name == student.name && students[i].sex == student.sex && students[i].hobbies.toString() == student.hobbies.toString()){
                    console.log('未作任何更新,沒法提交學生信息')
                }else{
                    students[i].name = student.name
                    students[i].sex = student.sex
                    students[i].hobbies = student.hobbies
                    isUpdate = true
                }
            }
        }
        // 根據 isUpdata標識,判斷是否有數據更新
        if(isUpdate){
            let dataStr = JSON.stringify({students:students})

            fs.writeFile(dataBase,dataStr,(err)=>{
                if(err){
                    return console.log('write to db.json failed...')
                }
                return console.log('跟新成功!')
            })
        }else{
            console.log('跟新失敗')
        }
    })
}

/** * 查找全部學生信息,經過callback回調函數將students數組返回 */
exports.searchAll = (callback)=>{
    fs.readFile(dataBase,(err,data)=>{
        if(err){
            return console.log('read file db.json err...')
        }
        callback(JSON.parse(data).students)
    })
}

/** * 經過id刪除學生信息 * Param: id:要刪除的學生id */
exports.delete = (id)=>{
     fs.readFile(dataBase,(err,data)=>{
        if(err){
            return console.log('readFiel db.json error...')
        }

        var students = JSON.parse(data).students
        var isDelete = false
        // 遍歷學生數組,刪除指定id的學生信息
        for(var i = 0;i<students.length;i++){
            if(students[i].id === id){
                students.splice(i,1)
                isDelete = true
            }
        }
        // 經過 isDelete標識判斷是否刪除成功
        if(isDelete){
            let dataStr = JSON.stringify({students:students})

            fs.writeFile(dataBase,dataStr,(err)=>{
                if(err){
                    return console.log('write to db.json failed...')
                }
                return console.log('刪除成功!')
            })
        }else{
            console.log('沒有找到要刪除的數據')
        }
    })
}
複製代碼
// test.js
const api = require('./StudentApi')

var student = {
     "id": 3,
    "name": "Kobe",
    "sex": "M",
    "hobbies": [
        "basketball",
        "swimming",
        "sleep"
    ]
}
// api.add(student,null)

// api.searchAll((data)=>{
// console.log(data)
// })

// api.delete(4)
// api.update(student)


複製代碼

七. express-middleware

**middleware:**即中間件,在express中是很重要的一個概念

**官網的介紹:**Express 是一個路由和中間件 Web 框架,其自身只具備最低程度的功能:Express 應用程序基本上是一系列中間件函數調用

**做用:**簡單來講,就是當咱們在瀏覽器中發送URL請求開始,到咱們在瀏覽器接收到數據和看到界面渲染爲止,這中間的全部過程都是經過 express中間件來完成的

7.1 前景回顧

其實在咱們以前的學習過程當中,咱們已經使用過express中間件了,如今咱們來一塊兒回顧一下

  • 當咱們在使用開發靜態資源的時候
  • 當咱們爲了獲取post請求的body參數的時候

這兩種場景都用到了express中間件,咱們發現,他們都有一個共同的特徵,即都是經過 app.use方法來調用的

const express = reuire('express')
const app = express()

// 開放靜態資源
app.use('/public/',express.static('./public'))
// 獲取post請求參數配置
app.use(bodyParser.urlencoded({extended:false}))
app.use(bodyParser.json())
複製代碼

7.2 中間件分類和基礎

分類:

express應用程序能夠執行以下幾種類型的中間件

  • 應用層中間件
  • 路由器層中間件
  • 錯誤處理中間件
  • 內置中間件
  • 第三方中間件

基礎:(重要)

中間件簡單來講就是一系列函數,就是在請求返回以前,咱們對數據進行的一系列處理邏輯的執行函數

每個函數都包含三個參數:req,res,next

  • req:包含請求對象相關信息

  • res:包含相應對象相關信息

  • next():控制中間件傳播流程

    • 這裏重點介紹一下next(),因爲存在中間件的堆棧關係,以及存在一個路徑能夠配置多個路由的作法存在,因此就須要咱們經過next()方法合理的控制中間件的調用
    // 這裏拿一箇中間件作示範
    
    // 以下,use方法裏面的多個函數即爲中間件的堆棧,能夠經過next方法,讓他們按照順序執行,若是前面的方法沒有調用next()方法,則後面的函數就不會執行,請求將會停留在該中間件中,不會繼續執行
    app.use('./demo',function(req,res,next){
        console.log('第一個中間件函數')
        next()
    },function(req,res,next){
        console.log('第二個中間件函數')
       // next()
    },function(req,res,next){
        console.log('第三個中間件函數,若是上面的方法沒有調用next(),則這裏不會執行')
        next()
    })
    
    // demo路徑的第二個路由,可否執行要依賴於前面的中間件是否調用了next()方法
    app.use('./demo',function(req,res,next){
        console.log('第二個路由的中間件1')
        next()
    },function(req,res,next){
        console.log('第二個路由的中間件2')
       // next()
    },function(req,res,next){
       res.send('hello world')
    })
    
    複製代碼

7.3 應用層中間件

使用 app.use()或者app.METHOD()函數將 中間件綁定到應用程序對象的實例,其中METHOD是中間函數處理的請求的小寫HTTP方法

**Demo1:**未指定路徑的中間件,全部的請求都會先執行該方法

const app = express()

app.use(function(req,res,next){
    console.log(Date.now())
    next()
})
複製代碼

**Demo2:**指定路徑上的中間件,只有符合指定路徑的請求才會調用該中間件

const app = express()

// 只有指定路徑下的請求才會調用該中間件
app.use('/a',function(req,res,next){
    console.log(Date.now())
    next()
})
複製代碼

**Demo3:**能夠經過添加多個function,實現中間件堆棧的調用效果

const app = express()

app.use('/user', function(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}, function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

app.get('/user',(req,res)=>{
    console.log('user目錄')
    res.send('user目錄')
})
複製代碼

結果:

Request URL: /user
Request Type: GET
user目錄
複製代碼

**Demo4:**爲同一個路徑定義多個路由,若是前面路由使請求結束,則後面路由不會調用,不然會按順序調用

// Demo4:爲同一個路徑定義多個路由,若是前面路由使請求結束,則後面路由不會調用,不然會按順序調用
app.get('/more',(req,res,next)=>{
    console.log('more第一個路由的第一次調用')
    next()
},function(req,res,next){
    // console.log('隨便打印點什麼')
    // next()
    res.send('這裏使請求結束,後續的路由將不會調用')

})

app.get('/more',(req,res,next)=>{
    console.log('more第二個路由的第一次調用')
    next()
},function(req,res,next){
    res.send('more第二個路由的第二次調用')
})
複製代碼

Demo5:

  • 能夠經過 next('route')方法,跳過路由中間件堆棧中的路由,將控制權交給下一個路由
  • 注意:next('route')方法只在GET,PUT,POST方法中有效
    • app.get(),app.put(),app.post()
    • router.get(),router.put(),router.post()
// Demo5:經過 next('route')方法跳出堆棧,將控制權交給下一個路由
app.get('/demo5',(req,res,next)=>{
    console.log('demo5第一個路由的第一次調用')
    next()
},function(req,res,next){
    console.log('welcome to demo5')
    //next()
    next('route')
},function(req,res,next){
    console.log("若是前面調用了next('route')方法,這裏將不會調用")
    next()
})

app.get('/demo5',(req,res,next)=>{
    console.log('demo5第二個路由的第一次調用')
    next()
},function(req,res,next){
    res.send('demo5第二個路由的第二次調用')
})
複製代碼

結果:

demo5第一個路由的第一次調用
welcome to demo5
demo5第二個路由的第一次調用

//瀏覽器展現
demo5第二個路由的第二次調用
複製代碼

7.4 路由中間件

路由中間件相似於應用層中間件,只不過在應用層中間件的基礎上,將路由模塊抽離出來,實現方法同上

這裏只作簡單Demo示例

Demo

const express = require('require')
const app = express()
const router = app.Router()

router.use('/user', function(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}, function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});

router.use('/user', function(req, res, next) {
  console.log(Date.now());
  next();
}, function (req, res, next) {
  res.end('hello world')
});
複製代碼

7.5 錯誤處理中間件

錯誤處理中間件用法同上,只有一點不一樣

錯誤處理中間件始終採用四個自變量。必須提供四個自變量,以將函數標識爲錯誤處理中間件函數。即便無需使用 next 對象,也必須指定該對象以保持特徵符的有效性。不然,next 對象將被解釋爲常規中間件,從而沒法處理錯誤。

Demo:

app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});
複製代碼

7.6 內置中間件

Express 中惟一內置的中間件函數是 express.static

**語法:**express.static(root, [options])

參數:

  • root 自變量指定從其中提供靜態資源的根目錄。
  • options包含一系列的參數配置

**做用:**配置應用程序的靜態文件目錄,可經過靜態路徑直接在瀏覽器獲取靜態文件目中的文件

詳情:Github:server-static

**Demo:**對於應用程序,能夠配置多個靜態目錄

app.use(express.static('public'));
app.use(express.static('uploads'));
app.use(express.static('files'));
複製代碼

7.7 第三方中間件

使用:

  • 第三方中間件屬於nodejs模塊,須要先安裝
  • 經過在應用層或路由器層的應用程序中將其加裝入

詳情:express官網:第三方中間件

**Demo:**第三方中間件 cookie-parser的使用

npm i cookie-parser -S
複製代碼
var express = require('express');
var app = express();
var cookieParser = require('cookie-parser');

// load the cookie-parsing middleware
app.use(cookieParser());
複製代碼
相關文章
相關標籤/搜索