首先分析一下express的路由都有什么
- 開放靜態(tài)資源訪問
- 對get,post請求進(jìn)行處理
express是怎么實(shí)現(xiàn)的
const express = require("express");
const app = express();
app.static("public")
app.get('/get', (req, res) => {……})
app.post("/post", (req, res) => {……})
先來實(shí)現(xiàn)對get以及post請求的處理
1.先定義一個(gè)route.js的文件,說白了就是定義一個(gè)路由模塊
首先在app.js中通過http模塊開啟一個(gè)服務(wù),監(jiān)聽的8081端口
var http = require('http');
http.createServer(function (request, response) {
response.writeHead(200, {'Content-Type': 'text/plain'});
response.end('Hello World');
}).listen(8081);
createServer函數(shù)中傳入一個(gè)回調(diào)函數(shù),回調(diào)函數(shù)有兩個(gè)參數(shù)request存有客戶端發(fā)來的請求信息,response服務(wù)器對客戶端的響應(yīng),在函數(shù)內(nèi)部通過獲取不同的request.url請求路徑進(jìn)行不同的處理和響應(yīng),所以我們在route.js中暴露一個(gè)app函數(shù),統(tǒng)一處理請求,那么http服務(wù)就可以直接寫成
const app = require('./route')
http.createServer(app).listen(8081);
在route.js中定義一個(gè)函數(shù)server,返回一個(gè)app函數(shù),這樣將變量定義在函數(shù)作用域防止污染全局。暴露server執(zhí)行,返回的就是app這個(gè)函數(shù)了
route.js
let server = () => {
let app = function (req, res) {
}
return app
}
module.exports = server()
2.處理get,post請求
因?yàn)樵赼pp.js中注冊路由的是app.get/app.post方式的方法,所以此時(shí)在app這個(gè)函數(shù)上定義兩個(gè)方法,不同的請求方法式處理不同,在server函數(shù)中再丁一一個(gè)對象G,對象G再定義兩個(gè)對象分別存儲定義的函數(shù)

let server = () => {
let G = {
_post: {},
_get: {}
}
let app = function (req, res) {
}
app.get = function (str, cb) {
G._get[str] = cb
}
app.post = function (str, cb) {
G._post[str] = cb
}
return app
}
以app.get()方法為例,我們將app暴露出去,在app.js中引入了這個(gè)模塊,所以在app.js中就可以調(diào)用app.get()方法了,因?yàn)槭且幚砺酚?,所以app.get函數(shù)的第一個(gè)參數(shù)就是路由,第二個(gè)參數(shù)就是傳入對應(yīng)的回調(diào)函數(shù)來處理這個(gè)路由的邏輯。app.get方法內(nèi)部就會對該回調(diào)函數(shù)進(jìn)行注冊,將str作為_get對象的屬性,回調(diào)函數(shù)就是屬性值了。
現(xiàn)在進(jìn)行一次測試
app.js
這里提前定義一個(gè)/login的路由,然后執(zhí)行app.js文件,在瀏覽器輸入"http://localhost:8081/login",如果頁面有“home”打印出來就證明成功了。
const app = require('./route')
var http = require('http');
http.createServer(app).listen(8081);
app.get('/login', (req, res) => {
res.end("home")
})
route.js
因?yàn)檫@里的app函數(shù)是作為http.createServer的回調(diào)函數(shù),我們不用手動(dòng)執(zhí)行,它自己就會執(zhí)行。在測試的時(shí)候事先注冊了/login函數(shù),所以在里面判斷一下G的_get對象里面有沒有這個(gè)函數(shù),如果有就執(zhí)行,所以他一執(zhí)行就會執(zhí)行我們?yōu)?/login'路由注冊的函數(shù),就會給客戶端返回字符串“home”
let server = () => {
let G = {
_post: {},
_get: {
}
}
let app = function (req, res) {
if(G._get['/login']){
G._get['/login'](req, res)
}
}
app.get = function (str, cb) {
G._get[str] = cb
}
app.post = function (str, cb) {
G._post[str] = cb
}
return app
}
module.exports = server()
3.實(shí)現(xiàn)route.js的app函數(shù)的功能
客戶端發(fā)來的請求可能是post可能是get,而post和get的路由在定義的時(shí)候也可能是一樣的
如
app.get('/login', (req, res) => {
res.end("home")
})
app.post('/login', (req, res) => {
res.end("home")
})
所以要根據(jù)請求方式和路由名稱來決定執(zhí)行哪一個(gè)方法
- 首先獲取請求的路由名稱和請求方法
利用url模塊的parse方法獲取請求路徑中的路由
const url = require('url')
let app = function (req, res) {
// 獲取請求的路由,就是如http://localhost:8081/login中的/login
let pathname = url.parse(req.url).pathname;
// 獲取請求方法
let method = req.method.toLowerCase();
}
- 根據(jù)路由和請求方法決定執(zhí)行哪個(gè)處理函數(shù)
let app = function (req, res) {
// 獲取請求的路由
let pathname = url.parse(req.url).pathname;
// 獲取請求方法,返回的是大寫的,這里轉(zhuǎn)成小寫了
let method = req.method.toLowerCase();
// 先判斷G的_get和_post對象里面有沒有對應(yīng)的路由,如果有向下執(zhí)行,沒有走else
if (G['_' + method][pathname]) {
// 判斷請求方法
if (method == "get") {
// get請求方法,執(zhí)行G._get對象中對應(yīng)的pathname方法,需要把req和res傳過去
G['_' + method][pathname](req, res); //執(zhí)行方法
} else {
// post方法,post請求參數(shù)是以流的形式傳遞,是一點(diǎn)一點(diǎn)的傳輸?shù)? let postData = '';
// post參數(shù)傳輸中
req.on('data', (chunk) => {
postData += chunk;
})
// post參數(shù)傳輸完畢,將參數(shù)綁定到req.body上,執(zhí)行G._post對象中對應(yīng)的函數(shù)
req.on('end', () => {
req.body = postData;
G['_' + method][pathname](req, res); //執(zhí)行方法
})
}
} else {
// 設(shè)置響應(yīng)頭,狀態(tài)碼404,文件類型是html,編碼字符集是utf-8
res.writeHead(404, { 'Content-Type': 'text/html;charset="utf-8"' });
// 提示頁面不存在
res.end('頁面不存在');
}
}
這樣就算初步完成了
再在res上擴(kuò)展一個(gè)send方法,用來統(tǒng)一設(shè)置響應(yīng)頭和響應(yīng)數(shù)據(jù),封裝一個(gè)changeRes方法,參數(shù)傳入res
function changeRes(res) {
res.send = (data) => {
res.writeHead(200, {
"Content-Type": "text/html;charset=utf-8"
})
res.end(data)
}
}
在app函數(shù)中直接調(diào)用一下changeRes方法就行了,傳入res
let app = function (req, res) {
//擴(kuò)展res的方法
changeRes(res);
}
然后在注冊路由時(shí)候,就可以直接使用了res.send()了,就不用另外再單獨(dú)設(shè)置響應(yīng)頭了
app.get('/login', (req, res) => {
res.send("home")
})
下一節(jié)
完整的router.js程序就是
const url = require('url')
function changeRes(res) {
res.send = (data) => {
res.writeHead(200, {
"Content-Type": "text/html;charset=utf-8"
})
res.end(data)
}
}
let server = () => {
let G = {
_post: {},
_get: {
}
}
const app = function (req, res) {
//擴(kuò)展res的方法
changeRes(res);
let pathname = url.parse(req.url).pathname;
let method = req.method.toLowerCase();
if (G['_' + method][pathname]) {
if (method == "get") {
G['_' + method][pathname](req, res); //執(zhí)行方法
} else {
let postData = '';
req.on('data', (chunk) => {
postData += chunk;
})
req.on('end', () => {
req.body = postData;
G['_' + method][pathname](req, res);
})
}
} else {
res.writeHead(404, { 'Content-Type': 'text/html;charset="utf-8"' });
res.end('頁面不存在');
}
}
app.get = function (str, cb) {
G._get[str] = cb
}
app.post = function (str, cb) {
G._post[str] = cb
}
return app
}
module.exports = server()