Express 是基於 Node.js 平臺,快速、開放、極簡
的 Web 開發框架。搭建web伺服器
Express 的本質:就是一個 npm 上的第三方包,提供了快速建立 Web 伺服器的便捷方法。
使用Express開發框架可以非常方便、快速的建立Web網站的伺服器或API介面的伺服器
官方網址:https://www.expressjs.com.cn/
下載安裝:
npm init -ynpm i express -S
使用步驟:
const express = require('express')const app = express()app.get(路由,回撥) // get是請求方法app.listen(埠號)
請求方法還支援:
get - 查詢請求 - 條件在位址列
post - 新增請求 - 資料在請求主體
put - 修改請求 - 條件在位址列 - 資料在請求主體
delete - 刪除請求 - 條件在位址列
各個動詞方法用來處理對應的請求。不過有一個方法除外:
app.all() // 可以用來處理任意請求方式
雖然all方法可以處理任意請求,但是儘量少用,甚至儘量不要使用。
使用postman進行偵錯
完全匹配
// 匹配根路徑的請求app.get('/', function (req, res) { res.send('root');});// 匹配 /about 路徑的請求app.get('/about', function (req, res) { res.send('about');});// 匹配 /random.text 路徑的請求app.get('/random.text', function (req, res) { res.send('random.text');});
不完全匹配
// 匹配 acd 和 abcdapp.get('/ab?cd', function(req, res) { res.send('ab?cd');});// 匹配 abcd、abbcd、abbbcd等app.get('/ab+cd', function(req, res) { res.send('ab+cd');});// 匹配 abcd、abxcd、abRABDOMcd、ab123cd等app.get('/ab*cd', function(req, res) { res.send('ab*cd');});// 匹配 /abe 和 /abcdeapp.get('/ab(cd)?e', function(req, res) { res.send('ab(cd)?e');});
字元 ?、+、* 和 () 是正規表示式的子集,- 和 . 在基於字串的路徑中按照字面值解釋。
正則匹配:
// 匹配任何路徑中含有 a 的路徑:app.get(/a/, function(req, res) { res.send('/a/');});// 匹配 butterfly、dragonfly,不匹配 butterflyman、dragonfly man等app.get(/.*fly$/, function(req, res) { res.send('/.*fly$/');});
使用一個回撥函數處理路由:
app.get('/example/a', function (req, res) { res.send('Hello from A!');});
多次處理:
app.get('/example/b', function (req, res, next) { console.log('這處理完之後會交給下一個函數處理'); next();}, function (req, res) { res.send('Hello from B!');});
使用回撥函數陣列處理路由:
var cb0 = function (req, res, next) { console.log('CB0') next()}var cb1 = function (req, res, next) { console.log('CB1') next()}var cb2 = function (req, res) { res.send('Hello from C!')}app.get('/example/c', [cb0, cb1, cb2])
混合使用函數和函數陣列處理路由:
var cb0 = function (req, res, next) { console.log('CB0') next()}var cb1 = function (req, res, next) { console.log('CB1') next()}app.get('/example/d', [cb0, cb1], function (req, res, next) { console.log('response will be sent by the next function ...') next()}, function (req, res) { res.send('Hello from D!')})
res.download() // 提示下載檔案。 res.end() // 終結響應處理流程。 res.json() // 傳送一個 JSON 格式的響應。 res.jsonp() // 傳送一個支援 JSONP 的 JSON 格式的響應。 res.redirect() // 重定向請求。 res.render() // 渲染檢視模板。 res.send() // 傳送各種型別的響應。 res.sendFile() // 以八位位元組流的形式傳送檔案。 res.sendStatus() // 設定響應狀態程式碼,並將其以字串形式作為響應體的一部分傳送。
download範例:
// 響應下載 - res.download(被下載的原始檔,下載後的檔名稱,回撥函數)res.download("./test.html",'b.html',err=>{ if(err){ console.log("下載失敗"); }else{ console.log("下載成功"); }})
json範例:
// 給使用者端響應json資料// res.json(json格式的資料)let obj = { name:"張三", age:12, wife:"翠花", children:['阿大','阿二','小明']}res.json(obj)
jsonp範例:
// 給使用者端發起的jsonp請求做響應,響應的是json資料// res.jsonp(json格式的資料)let obj = { name:"張三", age:12, wife:"翠花", children:['阿大','阿二','小明']}res.jsonp(obj)
redirect範例:
// res.redirect() 用來跳轉路由的 - /a這個路由,其實/b這個路由就正好可以處理他,就可以在/a這個路由處理中,將這次請求交給/b這個路由去處理res.redirect('/index')app.get('/index',(req,res)=>{ let data = fs.readFileSync('./test.html') res.end(data)})
send範例:
// res.send() - 用於給使用者端響應字串的 - 字串中如果是標籤,可解析成html - 自動設定資料型別和編碼let html = ` <h2>這是一個h2標籤</h2> `// res.end 不會自動設定資料型別,也不會設定編碼// res.end(html)res.send(html)
sendFile範例:
// res.sendFile() 用於給使用者端響應一個檔案res.sendFile(__dirname + '/test.html')
sendStatus範例:
// sendStatus是自動設定響應狀態碼,並將對應的響應狀態描述響應給使用者端 res.sendStatus(404) // 響應 not found res.sendStatus(200) // 響應 ok
req.url // 請求的路徑 - 如果有?傳參,這個路徑中也會帶有引數 req.method // 請求方法 req.path // 請求路徑 - 如果有?傳參,這個路徑中不包含引數 req.protocol // 協定 req.params // 獲取get請求的引數 - 針對動態路由傳參 - restful風格的引數 - 最終獲取到的是物件,物件的鍵,就是路徑指定的名稱 req.query // 獲取get請求的引數 - 針對傳統形式傳參 - 使用?引數 - 最終獲取到的是物件
express提供了一個非常好用的方法,叫做 express.static()
,通過此方法,可以非常方便地建立一個靜態web資源伺服器:
app.use(express.static('public')) // app.use()表示使用(中介軟體) // 現在可以存取public目錄下所有的檔案 // 如public/aa.jpg檔案,則可以通過 : http://xxxx/images/aa.jpg
express還支援給靜態資原始檔建立一個虛擬的檔案字首(實際上檔案系統中並不存在),可以使用 express.static
函數指定一個虛擬的靜態目錄,就像下面這樣:
字首的使用意義:
- 可以迷惑別人,一定程度上阻止別人猜測我們伺服器的目錄結構
- 可以幫助我們更好的組織和管理靜態資源
app.use('/static', express.static('public'))
字首前面的「/」必須要加,否則就錯。【404】
現在你可以使用 /static
作為字首來載入 public
資料夾下的檔案了:
http://localhost:3000/static/images/kitten.jpg http://localhost:3000/static/css/style.css http://localhost:3000/static/js/app.js http://localhost:3000/static/images/bg.png http://localhost:3000/static/hello.html
使用app.use()方法一般寫在具體的路由監聽之前。
每個應用可有多個靜態目錄。
app.use(express.static('public'))app.use(express.static('uploads'))app.use(express.static('files'))
路由在生活中如撥打服務電話時,按數位幾能處理什麼樣的處理,它就是類似於按鍵與服務之間的對映關係。在Express中,路由指的就是使用者端發起的請求(地址)與伺服器端處理方法(函數)之間的對映關係。
express中的路由分3部份組成,分別是請求型別(方法)、請求uri(地址)和對應的處理常式。
當一個使用者端請求到達伺服器端之後,先經過路由規則匹配,只有匹配成功之後,才會呼叫對應的處理常式。在匹配時,會按照路由的順序進行匹配,如果請求型別和請求的 URL 同時匹配成功,則 Express 會將這次請求,轉交給對應的函數進行處理。
app.<get/post/put/delete/use>(uri,(req,res)=>{})// use方法並不是請求型別方法,但是它放的位置與請求方法一致
含義:將原本可能寫在一個檔案中的路由規則,拆分成若干個路由檔案(js檔案,一個js檔案就是一個模組)。
顧名思義,將路由進行模組化,以模組(js檔案)為單位進行管理,物以類聚。
核心思想:能拆就拆(拆到不能拆為止,解耦,高內聚,低耦合)。
在開發專案時,如果將所有的路由規則都掛載到入口檔案中,程式編寫和維護都變得更加困難。所以express為了路由的模組化管理功能,通過express.Router()方法建立路由模組化處理程式,可以將不同業務需求分開到不同的模組中,從而便於程式碼的維護和專案擴充套件。
步驟:
指定那個路徑開頭的請求,用app.use(路徑開頭,處理模組)
app.use('/admin',adminRouter) // /admin開頭的請求路徑,交給adminRouter模組處理app.use('/front',frontRouter) // /front開頭的請求路徑,交給frontRouter模組處理
這個模組未定義,定義這個模組
let {adminRouter} = require('./admin/admin')let {frontRouter} = require('./front/front')
匯入的檔案還未定義,建立檔案
const express = require('express')const adminRouter = express.Router() // 建立路由物件// 通過路由物件處理剩餘的請求adminRouter.get('/goods',(req,res)=>{ res.send('這是admin模組的goods頁面')})// 匯出模組module.exports = {adminRouter}
此時,我們又兩種方案去處理請求:
同一個請求路徑使用不同的請求方法多次請求的簡寫:
app.route('/book') .get(function(req, res) { res.send('Get a random book'); }) .post(function(req, res) { res.send('Add a book'); }) .put(function(req, res) { res.send('Update the book'); });
中介軟體(middleware)可以理解為業務流程的中間處理環節,可以理解成中間過濾器。
中介軟體可以分類可分如下幾類:
內建中介軟體,也就是express本身自帶無需npm安裝
第三方中介軟體
非 Express 官方內建的,而是由第三方開發出來的中介軟體,叫做第三方中介軟體。在專案中可以通過npm進行安裝第三方中介軟體並設定,從而提高專案的開發效率。例如body-parser (解析post資料的)此中介軟體可以很方便幫助我們獲取到post提交過來的資料。
自定義中介軟體,開發者自己編寫的(中介軟體的本質其實就是一個function)
如果從使用層面去考慮,中介軟體可以劃分為:
express提供了好用的內建中介軟體,如提供一個靜態資源管理的中介軟體,通過此中介軟體就可以幫助為我們快速搭建一個靜態資源伺服器:
app.use('字首',express.static('託管目錄地址'))
在express中,除了內建的express.static()
中介軟體,還內建了另外2個常用的中介軟體:
express.json()
app.use(express.json())
req
請求物件的body
屬性上使用範例:
// 有了這個中介軟體以後,我們可以從使用者端給伺服器端傳送json資料,這個json資料會放到請求物件req的body屬性上app.use(express.json())app.post('/p1',(req,res)=>{ // express.json中介軟體可以讓資料掛在req的body屬性上 console.log(req.body); res.send('this p1 request is ended')})
首先必須是post請求,然後必須有資料,但是資料不能是以前的 x-www-form-urlencoded這樣的資料,必須是raw的資料
然後請求頭的content-type 必須是 application/json
express.urlencoded()
app.use(express.urlencoded({extended: false}))
req
請求物件的body
屬性上注意,
- 後面提及的這2個常用內建中介軟體存在相容性問題。
- 上述2箇中介軟體都說把資料處理之後掛到req.body上,但是實際上並不會出現我們想的覆蓋的問題。
案例:使用json、urlencoded中介軟體來接收json資料與表單post資料,傳送可以通過postman來進行
自定義中介軟體,其本質就是定義一個處理請求的函數,只是此函數中除了有request和response引數外還必須包含一個next引數,此引數作用讓中介軟體能夠讓流程向下執行下去直到匹配到的路由中傳送響應給使用者端。也可以通過給request物件新增屬性來進行中介軟體資料的向下傳遞
function mfn(req,res,next){ //. 自己需要定義的邏輯流程 // 中介軟體最後一定要執行此函數,否則程式無法向下執行下去 next()}
注意:在整個請求鏈路中,所有中介軟體與最終路由共用一份req
和res
案例:依據上述的共用特性,自定義一箇中介軟體來接收post提交的表單資料(意義:內建那中介軟體是不是存在相容性問題)
在express中,其允許我們使用第三方的中介軟體來進行對資料進行處理。比較典型的例如:可以使用第三方中介軟體來接收post資料。
以使用body-parser
中介軟體來接收post資料為例,步驟如下:
body-parser
npm i -S body-parser
body-parser
app.use(body.urlencoded({extended: false}))
req.body
獲數post中資料在使用的時候,
body-parser
庫的語法與前面看的express內建的express.urlencoded
中介軟體的語法非常相似,原因是內建中介軟體是基於body-parser
來實現的。
其他第三方中介軟體:http-proxy-middleware/cors/cookie-session …
**作用:**專門用來捕獲整個專案發生的異常錯誤,從而防止專案異常崩潰的問題產生(友好顯示異常)。
**格式:**錯誤級別中介軟體的函數引數中,必須有四個形參,分別是(err,req,res,next)
問:多出來的err引數有什麼作用呢?
答:裡面包含了錯誤的資訊,err.message屬性中就包含了錯誤的文字資訊,這個資訊可以在中介軟體中輸出給使用者看。
app.get('/',(req,res) => { throw new Error('伺服器內部發生了致命的錯誤!') res.send('Welcome to my homepage')})app.use((err,req,res,next) => { console.log('發生了錯誤:' + err.message) res.send('Error!' + err.message)})
**案例:**要求指定一個路徑(可能路由對應的檔案不存在),讀取檔案內容,輸出給使用者
注意事項:錯誤級別中介軟體要想發揮其作用,必須寫在所有的路由的後面,是否是app.listen
之前無所謂。
**作用:**用於處理404的請求響應
// 假設定義這個路由,但是實際請求的時候請求了/12345,這個時候就會404app.post("/1234", (req, res, next) => { res.send('你請求成功了')});// 404的輸出// 該中介軟體也需要寫在最後(與異常中介軟體的順序無所謂,只要確保其在所有的路由方法之後就可)app.use((req,res,next) => { // 輸出404錯誤 res.status(404).send('<h1>404</h1>') // 先指定404狀態碼,然後再輸出錯誤資訊})
404錯誤中介軟體也要求在所有的正常請求路由的後面去宣告使用,不要放在路由的前面,否則會導致後面的路由都是404錯誤。
**注意點:**錯誤級別的中介軟體,必須在所有路由之後註冊,至於404中介軟體與異常中介軟體,誰先誰後無所謂。
模板頁面:頁面:https://404.life/
cookie的原理是在瀏覽器中開闢了一個用來儲存http請求中的資料,第一次儲存之後,下次請求只要還是使用的當前瀏覽器,就能存取到瀏覽器這個空間中的資料。
cookie會作為鍵值對,在響應頭和請求頭之間攜帶。
cookie的特點:
npm i cookie-parser -S
使用:
// 匯入 const cookieParser = require('cookie-parser') // 中介軟體 app.use(cookieParser()); // 請求頭獲取 req.headers.cookie // 獲取所有cookie // 響應頭設定 res.cookie(鍵,值,{maxAge: 有效期-毫秒}) // 設定cookie
cookie是儲存在瀏覽器的,所以安全性不高,所以一些重要資料就不能儲存在cookie中,且cookie的儲存空間有限制,所以就有了session。
session是儲存伺服器端的,session需要依賴cookie,session資料儲存會在cookie中存放一個sessionid,這個sessionid會跟伺服器端之間產生對映關係,如果sessionid被篡改,他將不會跟伺服器端進行隱射,因此安全係數更高。且session的有效期比較短。通常是20分鐘左右,如果瀏覽器在20分鐘內沒有跟伺服器進行互動,伺服器就會刪除session資料。
npm i cookie-session -S
使用:
// 匯入:const session = require('cookie-session')// session設定session({ name:"sessionId", secret:"asdfasdfqwer", // 給sessioinId加密使用的祕鑰,隨便填寫 maxAge:20*60*1000 // 20分鐘})// 設定sessionreq.session[鍵] = 值// 獲取sessionreq.session[鍵]
npm i bcryptjs -S
使用:
var bcrypt = require('bcryptjs'); // 加密 密文 = bcryptjs.hashSync(明文[,數位]); // 數位,將使用指定的輪數生成鹽並將其使用。推薦 10 // 驗證 bcryptjs.compareSync(明文,密文); // 通過返回true,失敗返回false
npm install jsonwebtoken
使用:
// 加密生成tokenvar jwt = require('jsonwebtoken');var token = jwt.sign(被加密的物件, 鹽);// 驗證jwt.verify(token, 鹽, function(err, decoded) { // decoded是解密後的物件});
npm i multer -S
使用:
var multer = require('multer')var upload = multer({ dest: path.join(__dirname,'public','image') }) // 指定上傳的檔案路徑app.post('/profile', upload.single('上傳表單name值'), function (req, res, next) { // req.file 是上傳的檔案資訊 - 可以從中獲取到檔名稱、路徑、字尾 - 拼接路徑存入mongodb})
npm i mysql -S
使用:
// 匯入const mysql = require("mysql");// 建立連線物件const db = mysql.createConnection({ host:"localhost", user:"root", password:"root", database:"test"});// 連線db.connect(err=>{ if(err){ console.log("連線失敗,錯誤:"+err); return; } console.log("連線成功");});// 執行語句db.query("",(err,result)=>{ if(err){ console.log("失敗,錯誤:"+err); return; } console.log("成功"); console.log(result);});
npm i svg-captcha -S
使用:
const svgCaptcha = require('svg-captcha') // 建立驗證碼 let captcha = svgCaptcha.create(); // captcha是是一個物件,其中包含data鍵和text鍵,text是驗證碼上的字元,data是一個svg標籤直接可以顯示為一張圖片
npm install nodemailer --save
使用:
const nodemailer = require('nodemailer')// 1. 建立傳送器const transport = nodemailer.createTransport({ // 需要你傳送放郵箱的 stmp 域名和密碼和一些其他資訊 // 需要你去複製一下, 找到下載的 nodemailer 第三方包 // nodemailer -> lib -> well-known -> services.json "host": "smtp.qq.com", "port": 465, "secure": true, // 證明你的身份 auth: { // 傳送方郵箱的使用者名稱 user: '郵箱號', // stmp 允許密碼 pass: '授權碼' }})// 2. 傳送郵件transport.sendMail({ // 從那個郵箱傳送 from: '傳送方郵箱', // 傳送到哪裡, 可以寫一個字串, 書寫郵箱, 也可以寫一個陣列, 寫好多郵箱 to: ['接收方郵箱', '接收方郵箱'], // 郵件標題 subject: '標題', // 本次郵件的 超文字 內容 html: ` 您好: 本次的驗證碼是 <h1 style="color: red;"> 2345 </h1> 請在 3 分鐘內使用 <br> ------------------------<br> 前途無限股份有限公司 `, // 本次郵件的 文字 內容 // text: ''}, function (err, data) { if (err) return console.log(err) console.log('郵件傳送成功') console.log(data)})
在一個web應用程式中,如果只是使用伺服器端程式碼來編寫使用者端html程式碼,前後端不分離,那麼會造成很大的工作量,而且寫出來的程式碼會比較難以閱讀和維護。如果只是使用使用者端的靜態的HTML檔案,那麼後端的邏輯也會比較難以融入到使用者端的HTML程式碼中。為了便於維護,且使後端邏輯能夠比較好的融入前端的HTML程式碼中,同時便於維護,很多第三方開發者就開發出了各種Nodejs模板引擎,其中比較常用的就是Jade/Pug、Ejs和art-template 等模板引擎。
目的:使後端邏輯能夠比較好的融入前端的HTML程式碼中,同時便於維護
網址:
art-template 是一個簡約、超快的模板引擎。
開發模式:
傳統開發模式:
前端程式碼和後端程式碼寫在了一起
混合在了一起,但是這種檔案有要求,檔案通常都是後端語言的檔案中才能寫後端的邏輯 - 這樣的話,對於我們前端攻城獅特別不友好,因為我們前端攻城獅負責html頁面,但是在html頁面中不能寫後端的邏輯,如果將html頁面放在後端的檔案中,我們又需要學習後端語言
此時,模板引擎出現了 - 模板引擎其實就是讓我們可以在html頁面中書寫後端的邏輯 - 迴圈/判斷 …
模板引擎有很多種:jade/ejs/art-tamplate …
模板引擎最終能在html中寫邏輯,相當於我們程式碼前後端混合在一起的一種語法,最終為什麼能被瀏覽器所識別?因為瀏覽器在識別之前,我們的模板引擎需要進行編譯 - html程式碼
前後端分離開發模式:
html一個人去寫;
後端介面另一個人去寫;
雙方之間對接,使用介面檔案
html上所有的資料,都是通過ajax請求回來,並通過dom操作顯示在頁面中的
模板引擎渲染速度測試:
特性
下載安裝:
# 安裝 npm i -S art-template express-art-template
設定:
// 模板引擎設定 // 指定art-template模板,並指定模組字尾為.html app.engine('html', require('express-art-template')); // 指定模板檢視路徑 app.set('views', path.join(__dirname, 'views')); // 省略指定模組檔案字尾後名稱(可選,在渲染時可以省略的字尾) app.set('view engine', 'html')
art-template 支援**標準語法與原始語法**。標準語法可以讓模板易讀寫,而原始語法擁有強大的邏輯表達能力。標準語法支援基本模板語法以及基本 JavaScript 表示式;原始語法支援任意 JavaScript 語句,這和 Ejs一樣。
使用art-template展示一個檢視(html檔案)
將檢視放入views目錄下(允許分目錄)
編寫程式碼,展示檢視的方式是`res.redner(檔案的路徑)
app.get('/', (req, res) => { // 輸出檢視 res.render('404.html')})
控制層返回資料(在js控制層中賦值變數到檢視中)
app.get(uri,(req,res)=>{ res.render(模板,{ username: '張三', age: 25, gender: '女', hobby: ['籃球','乒乓球','羽毛球'] })})
變數輸出:
<!-- 標準語法 -->{{ username }}<!-- 或者 --><!-- 原始語法 --><%= username %>
在預設情況下,上述輸出方式不能將帶有HTML標記的內容讓瀏覽器解析,只會原樣輸出。如果需要將HTML標記讓瀏覽器,則請使用下述方式輸出資料:
<!-- 標準語法 -->{{@ username}}<!-- 原始語法 --><%- username %>
條件判斷:
{{if 條件}} … {{else if 條件}} … {{/if}} <%if (條件){%> … <%}else if (條件){%> … <%}%>
迴圈:
<!-- 支援 陣列和物件的迭代 預設元素變數為$value 下標為$index 可以自定義 {{each target val key}}-->{{each 迴圈的資料}} {{$index}} {{$value}} {{/each}} {{each 迴圈的資料 val key}} {{key}} {{val}} {{/each}} <% for(var i = 0; i < target.length; i++){ %> <%= i %> <%= target[i] %> <% } %>
如果使用預設的鍵、值的名字(index,value)則其前面的
$
一定要寫!一定要寫!!!如果使用的自定義的鍵、值的名字,則前面的
$
一定不能寫!!不能寫!!
模板匯入:
{{include '被引入檔案路徑'}} <% include('被引入檔案路徑') %>
- 如果是當前路徑下的一定要寫
./
,不寫則從磁碟根下開始尋找- 被include的檔案字尾名預設為
.art
,如果不是請勿省略- 在子模板中最好不要有html、head和body標籤(否則容易出現樣式錯誤的問題)
模板繼承:
被繼承的模板:
<html><head> <meta charset="utf-8"> <!-- 類似於vue的插槽 --> <title>{{block 'title'}}My Site{{/block}}</title></head><body> <!-- block預留位置 content此佔位的名稱 --> {{block 'content'}}{{/block}}</body></html>
需要繼承的子模板:
<!--extend 繼承 -->{{extend './layout.html'}} {{block 'title'}}首頁{{/block}} {{block 'content'}} <p>This is just an awesome page.</p>{{/block}}
bootcss線上構建器:https://www.bootcss.com/p/layoutit/
更多程式設計相關知識,請存取:!!
以上就是建議收藏!帶你手把手教你入門express的詳細內容,更多請關注TW511.COM其它相關文章!