一巍扛、路由
1. 什么是路由
(1)路由就是映射關(guān)系。
(2)在 Express 中熔萧,路由指的是客戶端的請求與服務器處理函數(shù)之間的映射關(guān)系门躯。
2. express路由的組成
客戶端的請求地址、請求方式和服務器的處理函數(shù)杂抽。
app.get/post( ' URL ' 诈唬, function(){ } )
3. 路由的匹配過程
(1)每當一個請求到達服務器之后,需要先經(jīng)過路由的匹配缩麸。
(2)只有客戶端發(fā)送的請求方式和請求地址都匹配成功后铸磅,才會調(diào)用對應的服務器處理函數(shù)。
4. 路由的簡單使用
直接將路由掛載到服務器上杭朱。
const express = require('express')
const app = express()
// 掛載路由
app.get('/', (req, res) => {
res.send('hello world.')
})
app.post('/', (req, res) => {
res.send('Post Request.')
})
app.listen(80, () => {
console.log('http://127.0.0.1')
})
5. 路由的模塊化使用
不要把路由直接掛載到服務器上阅仔,而是將路由抽離為單獨的模塊。
① 創(chuàng)建路由模塊對應的 .js 文件
② 調(diào)用 express.Router() 函數(shù)創(chuàng)建路由對象
③ 向路由對象上掛載具體的路由
④ 使用 module.exports 向外共享路由對象
// 1.導入express包
const express = require('express')
// 2.創(chuàng)建路由的實例對象
const router = express.Router();
// 3.掛載具體的路由
router.get('/user/list', (req, res) => {
res.send('獲取列表成功')
})
router.post('/user/add', (req, res) => {
res.send('添加列表成功')
})
// 4.共享路由對象
module.exports = router;
⑤ 創(chuàng)建服務器的js文件弧械,在服務器的js文件中使用 app.use() 函數(shù)注冊路由模塊八酒。
// 導入express包
const express = require('express');
// 創(chuàng)建服務器實例對象
const app = express();
// 5.注冊路由模塊
const userRouter = require('./2.路由模塊');
app.use('/api',userRouter); //為路由添加URL前綴
// 啟動服務器
app.listen(80, () => {
console.log('express server running at http://127.0.0.1:80');
});
二、中間件
1. 什么是中間件
(1)定義:中間件(Middleware )刃唐,特指業(yè)務流程的中間處理環(huán)節(jié)羞迷。
(2)執(zhí)行:當一個請求到達 Express 的服務器之后,首先會依次調(diào)用多個中間件對這次請求進行預處理画饥。然后才會調(diào)用路由函數(shù)衔瓮。
2. 中間件的格式
(1)Express中的中間件本質(zhì)上就是一個function處理函數(shù)。
(2)中間件函數(shù)的參數(shù)有三個req抖甘、res热鞍、next。
(3)路由函數(shù)的參數(shù)只有兩個req和res衔彻。
app.get( ' / ' 薇宠,function( req ,res艰额, next ) { next( ) } )
這里的function函數(shù)就叫中間件澄港。
const express = require('express');
const app = express();
// 定義一個最簡單的中間件函數(shù)
var mw = function (req, res, next) {
console.log('這是一個最簡單的中間件函數(shù)');
// 當前中間件處理結(jié)束后必須調(diào)用next()函數(shù)
next();
};
app.listen(80, () => {
console.log('express running at http://127.0.0.1:80');
});
注意:
①中間件必須有參數(shù)next函數(shù),因為上一個中間件的輸出會作為下一個中間件的輸入柄沮。
②當前中間件的業(yè)務處理完畢后慢睡,必須調(diào)用next( )函數(shù)逐工。
3. 全局生效的中間件函數(shù)
app.use( 中間件函數(shù) ) (是函數(shù),別忘了小括號())
app.use(function (req, res, next) {
console.log('這是一個最簡單的中間件函數(shù)');
// 當前中間件處理結(jié)束后必須調(diào)用next()函數(shù)
next();
});
4. 中間件的作用
(1)多個中間件和路由共享相同的req和res對象漂辐。
(2)基于此特性,可以在上游的中間件上為req或res對象添加自定義屬性或方法棕硫,供下游中間件和路由使用髓涯。
// 導入express包,創(chuàng)建服務器實例對象
const express = require('express');
const app = express();
// 創(chuàng)建第一個全局生效的中間件
app.use(function (req, res, next) {
const time = +new Date(); //獲取當前時間戳
req.time = time; //為req對象掛載自定義屬性
console.log('第一個中間件被執(zhí)行');
next();
});
// 創(chuàng)建第二個全局生效的中間件
app.use(function (req, res, next) {
console.log('第二個中間件被執(zhí)行');
next();
});
// 路由
app.get('/', function (req, res) {
res.send('根地址的get請求成功' + req.time);
});
// 啟動服務器
app.listen(80, () => {
console.log('express server running at http://localhost');
});
5. 局部生效的中間件
不使用app.use( )方法進行注冊哈扮,而是在指定路由里面調(diào)用的中間件叫局部中間件纬纪。例如:
app.get( ' /user ' ,mw1 滑肉,mw2 包各,(req ,res)=>{ } )
app.get( ' /user ' 靶庙,[mw1 问畅,mw2] ,(req 六荒,res)=>{ } )
const express = require('express');
const app = express();
// 創(chuàng)建第一個局部生效的中間件
const mw1 = function (req, res, next) {
console.log('第一個局部生效的中間件被調(diào)用了');
next();
};
// 創(chuàng)建第二個局部生效的中間件
const mw2 = function (req, res, next) {
console.log('第二個局部生效的中間件被調(diào)用了');
next();
};
// 創(chuàng)建路由护姆,調(diào)用局部中間件
app.get('/user', mw1,mw2, function (req, res) {
res.send('/user地址的get請求成功');
});
app.listen(80, () => {
console.log('express server running at http://localhost');
});
6. 中間件的注意事項
(1)除了錯誤級別的中間件,其它必須在路由之前注冊中間件掏击。
(2)客戶端發(fā)送的請求卵皂,可以連續(xù)調(diào)用多個中間件。
(3)一個中間件業(yè)務處理完成后砚亭,必須調(diào)用next( )函數(shù)灯变。
(4)next( )函數(shù)后面不能再寫代碼。
(5)被調(diào)用的中間件以及路由之間共享相同的req和res對象捅膘。
特殊場景:服務器暫停服務時添祸,用中間件攔截所有請求,第一個中間件不調(diào)用next( )篓跛。
三膝捞、中間件的分類
1. 應用級別的中間件
綁定到app服務器上的中間件就叫應用級別的中間件。
比如app.use( )綁定的全局中間件愧沟;app.get( )和app.post( )綁定的局部中間件蔬咬。
2. 路由級別的中間件
綁定到路由上的中間件就叫路由級別的中間件。
作用和 應用級別的中間件沒有差別沐寺。
3. 錯誤級別的中間件
定義全局生效的錯誤級別的中間件林艘,專門捕獲服務器的錯誤,防止服務器崩潰混坞。
app.use( function( err 狐援,req 钢坦,res,next ){ next( ) }
注意:①錯誤級別的中間件必須寫在所有路由的后面啥酱。
②錯誤級別的中間件函數(shù)有四個參數(shù)晦嵌。
const express = require('express');
const app = express();
// 1.路由
app.get('/', (req, res) => {
throw new Error('服務器法生了錯誤');
res.send('get請求成功');
});
// 2.全局生效的錯誤級別的中間件
app.use(function (err, req, res, next) {
res.send('捕捉到了服務器的錯誤');
next();
})
app.listen(80, () => {
console.log('express server running at http://localhost');
});
4.Express的內(nèi)置中間件
(1)express.static()方法可以創(chuàng)建一個靜態(tài)資源服務器。詳情見第三天筆記锭环。
(2)express.json( )方法解析JSON格式的請求體數(shù)據(jù)抄囚。(有兼容性,僅在4.16.0+ 版本中可用)
補充:req.body可以接收客戶端提交的數(shù)據(jù)绘趋。
如果沒有配置任何解析數(shù)據(jù)的中間件颤陶,req.body默認為underfined
app.use(express.json( ) )
const express = require('express');
const app = express();
// 1.創(chuàng)建用來解析JSON格式數(shù)據(jù)的內(nèi)置中間件
app.use(express.json());
// 2.路由
app.post('/user', (req, res) => {
console.log(req.body); //req.body可以接收客戶端提交的數(shù)據(jù)
res.send('post請求發(fā)送成功');
});
app.listen(80, () => {
console.log('express server running at http://localhost');
});
(3) express.urlencoded( )方法可以 解析URL-encoded 格式的請求體數(shù)據(jù)。
也就是form表單提交數(shù)據(jù)的鍵值對格式陷遮。
∽易摺(有兼容性,僅在4.16.0+ 版本中可用)
app.use(express.urlencoded( {extended: false}) )
5. 第三方的中間件
非 Express 官方內(nèi)置的中間件帽馋,而是由第三方開發(fā)出來的Express 中間件搅方,叫做第三方中間件。在項目中茬斧,可以按需下載并配置第三方中間件腰懂,從而提高項目的開發(fā)效率。
例如:除了使用 express.urlencoded 這個內(nèi)置中間件來解析請求體數(shù)據(jù)项秉,還可以使用body-parser 這個第三方中間 件绣溜,來解析請求體數(shù)據(jù)。使用步驟如下:
① 下載 npm install body-parser中間件
② 使用 require 導入中間件
③ 調(diào)用 app.use() 將中間件注冊為全局生效娄蔼。
注意:Express 內(nèi)置的 express.urlencoded中間件怖喻,就是基于body-parser 這個第三方中間件進一步封裝出來的。
const express = require('express');
const app = express();
// 1.導入第三方中間件
const parse = require('body-parse');
// 2.將第三方中間件注冊為全局生效的中間件
app.use(parse.urlencoded({ extended: false }));
// 3.路由
app.post('/user', (req, res) => {
console.log(req.body); //req.body可以接收客戶端提交的數(shù)據(jù)
res.send('post請求發(fā)送成功');
});
app.listen(80, () => {
console.log('express server running at http://localhost');
});
四岁诉、自定義解析url-encoded(form表單)數(shù)據(jù)的中間件
1.主體內(nèi)容的編寫:
(1)定義中間件函數(shù)锚沸。
(2)監(jiān)聽req的data事件。
data事件可以獲取到客戶端發(fā)送地請求體數(shù)據(jù)涕癣。
但是客戶端的數(shù)據(jù)分多次發(fā)送哗蜈,所以要定義一個變量str用來把每次的數(shù)據(jù)進行拼接。
(3)監(jiān)聽req的end事件坠韩。
當客戶端最終把所有數(shù)據(jù)發(fā)送到服務器之后距潘,會自動觸發(fā) req 的 end 事件。
此時只搁,導入Node.js的內(nèi)置模塊querystring音比,可以將查詢字符串轉(zhuǎn)換成對象格式。
(4)將處理好的數(shù)據(jù)對象掛載到req對象上氢惋。
// 導入querystring模塊
const qs = require('querystring');
// 自定義中間件函數(shù)
function bodyParse(req, res, next) {
// (1)定義變量str用來接收客戶端發(fā)送的請求體數(shù)據(jù)
var str = '';
// (2)監(jiān)聽req的data事件
req.on('data', function (chunk) {
str += chunk;
});
// (3)監(jiān)聽req的end事件
req.on('end', function () {
// (4)使用Node.js的內(nèi)置模塊querystring洞翩,將查詢字符串轉(zhuǎn)換成對象格式
const body = qs.parse(str);
// (5)將獲取到的數(shù)據(jù)對象掛載到req對象上稽犁,供下游路由使用
req.body = body;
next();
});
}
// 將自定義中間件函數(shù)對外共享
module.exports = bodyParse;
2.將自定義中間件封裝為模塊
類似于路由的模塊化使用:
(1)創(chuàng)建存放自定義中間件的.js文件。
(2)用module.exports骚亿,將寫好的自定義中間件函數(shù)對外共享已亥。
(3)在服務器的.js文件中導入中間件模塊。
(4)使用app.use( )方法来屠,把中間件注冊為全局有效陷猫。
const express = require('express');
const app = express();
// 導入自定義中間件模塊
const p = require('./9.自定義解析數(shù)據(jù)的中間件');
// 將中間件注冊為全局生效
app.use(p);
// 路由
app.post('/user', (req, res) => {
res.send(req.body); //req.body可以接收客戶端提交的數(shù)據(jù)
});
app.listen(80, () => {
console.log('express server running at http://localhost');
});
五、使用 Express 寫接口(實現(xiàn)跨域請求)
(1)創(chuàng)建路由模塊的.js文件的妖。
(2)在路由模塊中掛載響應客戶端的路由。
(3)將路由對象對外共享足陨。
// 導入express包
const express = require('express');
// 創(chuàng)建路由實例對象
const apiRouter = express.Router();
// 1.掛載路由,響應get請求
apiRouter.get('/get', (req, res) => {
// (1)獲取用戶通過查詢字符串嫂粟,發(fā)送到服務器的數(shù)據(jù)
const query = req.query;
// (2)響應客戶端
res.send({
status: 0,
msg: 'GET請求成功',
data: query,
});
});
// 2.掛載路由,響應post請求
apiRouter.post('/post', (req, res) => {
// (1獲取用戶通過請求體墨缘,發(fā)送到服務器的url-encoded數(shù)據(jù)
const body = req.body;
// (2)響應客戶端
res.send({
status: 0,
msg: 'POST請求成功',
data: body,
});
});
// 把路由對象對外共享
module.exports = apiRouter;
(4)創(chuàng)建基本的服務器星虹。
(5)在服務器.js文件中導入路由模塊。
// 導入express包
const express = require('express');
// 創(chuàng)建服務器實例對象
const app = express();
//導入路由模塊
const apiRouter = require('./12.創(chuàng)建接口的路由模塊');
// 導入解析url-encoded格式數(shù)據(jù)的內(nèi)置中間件
app.use(express.urlencoded({ extended: false }));
// 導入cors中間件镊讼,解決接口跨域問題
const cors = require('cors');
// 必須在配置cors之前宽涌,創(chuàng)建JSONP接口
app.get('api/jsonp', (req, res) => {
// (1)獲取客戶端發(fā)送的回調(diào)函數(shù)的名稱
const funName = req.query.callback;
// (2)定義響應給客戶端的數(shù)據(jù)對象
const data = { name: '吳磊', age: 22 };
// (3)根據(jù)前兩步,拼接出函數(shù)調(diào)用的JSON字符串
const scriptStr = `${funName}(${JSON.stringify(data)})`;
// (4)將拼接好的JSON字符串響應給客戶端
res.send(scriptStr);
});
// 將cors配置為全局生效
app.use(cors());
// 將路由注冊為全局生效
app.use('/api', apiRouter);
// 啟動服務器
app.listen(80, () => {
console.log('express server running at http://localhost');
});
六蝶棋、接口的跨域請求
1. 在線版jQuery
2.使用 cors 中間件解決跨域問題
cors 是 Express 的一個第三方中間件卸亮。通過安裝和配置cors 中間件玩裙,可以很方便地解決跨域問題。
使用步驟分為如下3 步:
① 運行 npm install cors 安裝中間件
② 使用 const cors = require('cors') 導入中間件
③ 在路由之前調(diào)用app.use( )方法 配置中間件吃溅。(注意格式!>龀蕖!)
app.use( cors())
七枉圃、 了解cors中間件
1. 什么是 CORS
CORS (Cross-Origin Resource Sharing,跨域資源共享)由一系列傳輸?shù)腍TTP 頭組成讯蒲,這些HTTP 頭決定瀏 覽器是否阻止前端JavaScript 代碼獲取跨域請求的響應。
同源安全策略默認阻止“跨域”獲取資源墨林。但是CORS 給了 web 服務器這樣的權(quán)限,即服務器可以選擇旭等,允許跨 域請求訪問到它們的資源酌呆。
2. CORS 的注意事項
① CORS 主要在服務器端進行配置。客戶端瀏覽器無須做任何額外的配置搔耕,即可請求開啟了 CORS 的接口隙袁。
② CORS 在瀏覽器中有兼容性。只有支持XMLHttpRequest Level2 的瀏覽器弃榨,才能正常訪問開啟了 CORS 的服 務端接口(例如:IE10+菩收、Chrome4+、FireFox3.5+)
3. CORS 響應頭部 :Access-Control-Allow-Origin
(1)響應頭部中可以攜帶一個 Access-Control-Allow-Origin字段鲸睛。
(2)origin 參數(shù)的值指定了允許訪問該資源的外域URL娜饵。
(3)如果origin參數(shù)的值為通配符*,表示允許來自任何域的請求官辈。
res.setHeader( " Access-Control-Allow-Origin " 箱舞," http:www.baidu.com" )
4. CORS 響應頭部 :Access-Control-Allow-Headers
默認情況下,CORS 僅支持客戶端向服務器發(fā)送如下的9 個請求頭:
Accept拳亿、Accept-Language晴股、Content-Language、DPR肺魁、Downlink电湘、Save-Data、Viewport-Width万搔、Width 胡桨、 Content-Type (值僅限于 text/plain、multipart/form-data瞬雹、application/x-www-form-urlencoded 三者之一)
如果客戶端向服務器發(fā)送了額外的請求頭信息昧谊,則需要在服務器端,通過Access-Control-Allow-Headers 對額外 的請求頭進行聲明酗捌,否則這次請求會失斈匚堋!
res.setHeader( " Access-Control-Allow-Headers " 胖缤,"Content-Type尚镰,X-Custom-Header" )
5. CORS 響應頭部 :Access-Control-Allow-Methods
默認情況下,CORS 僅支持客戶端發(fā)起GET哪廓、POST狗唉、HEAD 請求。
如果客戶端希望通過 PUT涡真、DELETE等方式請求服務器的資源分俯,則需要在服務器端,通過Access-Control-Alow-Methods 來指明實際請求所允許使用的 HTTP 方法吗铐。
res.setHeader( " Access-Control-Allow-Methods " 唬渗," POST镊逝,GET蹋半,HEAD,DELETE" )
6. 簡單請求
同時滿足以下兩大條件的請求捻爷,就屬于簡單請求:
① 請求方式:GET也榄、POST甜紫、HEAD 三者之一
② HTTP 頭部信息不超過以下幾種字段:無自定義頭部字段囚霸、Accept拓型、Accept-Language劣挫、Content-Language压固、DPR帐我、 Downlink、Save-Data点弯、Viewport-Width抢肛、Width 捡絮、Content-Type(只有三個值application/x-www-formurlencoded福稳、multipart/form-data的圆、text/plain)
簡單請求的特點:客戶端與服務器之間只會發(fā)生一次請求。
7. 預檢請求
只要符合以下任何一個條件的請求钮糖,都需要進行預檢請求:
① 請求方式為 GET店归、POST且叁、HEAD 之外的請求Method 類型
② 請求頭中包含自定義頭部字段
③ 向服務器發(fā)送了application/json 格式的數(shù)據(jù)
在瀏覽器與服務器正式通信之前谴古,瀏覽器會先發(fā)送OPTION 請求進行預檢掰担,以獲知服務器是否允許該實際請求带饱,所以這一 次的 OPTION 請求稱為“預檢請求”勺疼。服務器成功響應預檢請求后执庐,才會發(fā)送真正的請求轨淌,并且攜帶真實數(shù)據(jù)盟步。
預檢請求的特點:OPTION 預檢請求成功之后却盘,才會發(fā)起真正的請求媳拴。
八屈溉、 JSONP接口的定義與使用
1. JSONP 的概念與特點
概念:瀏覽器端通過 <script> 標簽的 src 屬性语婴,請求服務器上的數(shù)據(jù)砰左,同時缠导,服務器返回一個函數(shù)的調(diào)用溉痢。這種請求數(shù)據(jù) 的方式叫做 JSONP孩饼。
特點:JSONP 僅支持 GET 請求立膛,不支持POST、PUT好啰、DELETE 等請求框往。
2. 實現(xiàn) JSONP 接口的步驟
如果項目中已經(jīng)配置了 CORS 跨域資源共享椰弊,為了防止沖突男应,必須在配置CORS 中間件之前聲明JSONP的接口沐飘。否則 JSONP 接口會被處理成開啟了CORS 的接口耐朴。
① 獲取客戶端發(fā)送過來的回調(diào)函數(shù)的名字
② 得到要通過 JSONP 形式發(fā)送給客戶端的數(shù)據(jù)
③ 根據(jù)前兩步得到的數(shù)據(jù)筛峭,拼接出一個函數(shù)調(diào)用的字符串
④ 把上一步拼接得到的字符串影晓,響應給客戶端的<script> 標簽進行解析執(zhí)行
// 必須在配置cors之前挂签,創(chuàng)建JSONP接口
app.get('api/jsonp', (req, res) => {
// (1)獲取客戶端發(fā)送的回調(diào)函數(shù)的名稱
const funName = req.query.callback;
// (2)定義響應給客戶端的數(shù)據(jù)對象
const data = { name: '吳磊', age: 22 };
// (3)根據(jù)前兩步饵婆,拼接出函數(shù)調(diào)用的JSON字符串
const scriptStr = `${funName}(${JSON.stringify(data)})`;
// (4)將拼接好的JSON字符串響應給客戶端
res.send(scriptStr);
});
3.使用jQuery調(diào)用JSONP接口
具體格式見Ajax第四天筆記。
補充
(1)服務器的app.use( )方法專門用來注冊全局有效的中間件或者路由灌灾。
(2)express.static( )內(nèi)置中間件和路由在用app.use( )注冊時锋喜,可以添加前綴。
(3)req.query可以獲取客戶端通過查詢字符串直颅,發(fā)送到服務器的數(shù)據(jù)功偿。
(4)req.body客戶獲取客戶端通過請求體械荷,發(fā)送到服務器的數(shù)據(jù)虑灰。數(shù)據(jù)格式為JSON穆咐、url-encoded等对湃,獲取后需要通過中間件解析后才能讀取心傀。