express功能極簡(jiǎn)箫攀,主要是路由和中間件構(gòu)成,在本質(zhì)上幼衰,其實(shí)就是調(diào)用各種中間件靴跛。
我先把API簡(jiǎn)單的列出來(lái):
- express() // 返回一個(gè)Application
- express.static(root, [options) // 內(nèi)置的唯一個(gè)中間件,負(fù)責(zé)托管應(yīng)用內(nèi)的靜態(tài)資源
- Application
- app.locals
- app.mountpath // 子應(yīng)用掛在的路徑
- app.on('mount', callback(parent))
- app.all(path, callback[, callback...]); //匹配所有的http動(dòng)詞
- app.(delete get post put METHOD...)(path, callback[, callbacks]);
- app.disable(name)
- app.disabled()
- app.enable(name)
- app.enabled()
- app.engine(ext, callback);
- app.listen(port[, hostname][, backlog][, callback])
- app.param([name, ]callback)
- app.path()
- app.render(view[, locals], callback)
- app.route(path)
- app.set(name, value)
- app.use([path,]function[, function...])
- req
- req.app
- req.baseUrl
- req.body
- req.cookies
- req.fresh
- req.hostname
- req.ip
- req.ips
- req.originalUrl
- req.params
- req.path
- req.protocol
- req.qurey
- req.route
- req.secure
- req.signedCookies
- req.stale
- req.subdomains
- req.xhr
- req.accepts(types)
- req.acceptsCharsets(charset [, ...])
- req.acceptsEncodings(encoding [, ...])
- req.acceptsLanguages(lang [, ...])
- req.get(field)
- req.is(type)
- req.param(name [, defaultValue])
- res.app
- res.headersSend
- res.locals
- res.append(field [, value])
- res.attachment([filename])
- res.clearCookie(name [, options])
- res.download(path [, filename] [, fn])
- res.end([data] [, encoding])
- res.format(object)
- res.get(field)
- res.json([body])
- res.jsonp([body])
- res.links(links)
- res.location(path)
- res.redirect([status,] path)
- res.render(view [, locals] [, callback])
- res.send([body])
- res.sendFile(path [, options] [, fn])
- res.sendStatus(statusCode)
- res.set(field [, value])
- res.status(code)
- res.type(type)
- res.vary(field)
- Router
- express.Router([options])
- router.all(path, [callback, ...] callback)
- router.METHOD(path, [callback, ...] callback)
- router.param([name,] callback)
- router.route(path)
- router.use([path], [function, ...] function)
以上就是express的全部api
路由功能
-
路由就是根據(jù)不同的路徑和方法渡嚣,返回不同的內(nèi)容梢睛。
在express中是這樣的:
let app = express();
app.get('/hello', function(req, res){
console.log(req.query); // {name:'zfpx',age:8}
console.log(req.path); // /user
console.log(req.hostname);//主機(jī)機(jī)
res.end('hello');
})
// *表示所有路徑
app.get('*', function(req, res){
res.end('hello');
})
app.listen(8080, function(){
console.log('服務(wù)器已啟動(dòng)');
})
app.get('/uesrage/:userid/:age', function (req, res) {
console.log(req.user);
req.user.age = req.params.age;
setUser(req.user);
res.end('update age successfully');
});
中間件
原生 Node 的單一請(qǐng)求處理函數(shù),隨著功能的擴(kuò)張勢(shì)必會(huì)變的越來(lái)越難以維護(hù)识椰。而 Express 框架則可以通過(guò)中間件的方式按照模塊和功能對(duì)處理函數(shù)進(jìn)行切割處理绝葡。這樣拆分后的模塊不僅邏輯清晰,更重要的是對(duì)后期維護(hù)和開(kāi)發(fā)非常有利腹鹉。
實(shí)現(xiàn)
express.js藏畅。相當(dāng)于提供了一個(gè)工廠函數(shù),創(chuàng)建app
const Router = require('./router');
const Application = require('./application');
function createApplicaton() {
return new Application();
}
createApplicaton.Router = Router;
module.exports = createApplicaton;
application.js
//實(shí)現(xiàn)Router 和應(yīng)用的分離
const Router = require('./router');
const http = require('http');
const methods = require('methods');//['get','post']
console.log(methods);
const slice = Array.prototype.slice;
// methods = http.METHODS
function Application() {
this.settings = {};//用來(lái)保存參數(shù)
this.engines = {};//用來(lái)保存文件擴(kuò)展名和渲染函數(shù)的函數(shù)
}
Application.prototype.lazyrouter = function () {
if (!this._router) {
this._router = new Router();
}
}
Application.prototype.param = function (name, handler) {
this.lazyrouter();
this._router.param.apply(this._router, arguments);
}
// 傳二個(gè)參數(shù)表示設(shè)置功咒,傳一個(gè)參數(shù)表示獲取
Application.prototype.set = function (key, val) {
if (arguments.length == 1) {
return this.settings[key];
}
this.settings[key] = val;
}
//規(guī)定何種文件用什么方法來(lái)渲染
Application.prototype.engine = function (ext, render) {
let extension = ext[0] == '.' ? ext : '.' + ext;
this.engines[extension] = render;
}
methods.forEach(function (method) {
Application.prototype[method] = function () {
if (method == 'get' && arguments.length == 1) {
return this.set(arguments[0]);
}
this.lazyrouter();
//這樣寫(xiě)可以支持多個(gè)處理函數(shù)
this._router[method].apply(this._router, slice.call(arguments));
return this;
}
});
Application.prototype.route = function (path) {
this.lazyrouter();
//創(chuàng)建一個(gè)路由愉阎,然后創(chuàng)建一個(gè)layer ,layer.route = route.this.stack.push(layer)
this._router.route(path);
}
//添加中間件,而中間件和普通的路由都是放在一個(gè)數(shù)組中的力奋,放在this._router.stack
Application.prototype.use = function () {
this.lazyrouter();
this._router.use.apply(this._router, arguments);
}
Application.prototype.listen = function () {
let self = this;
let server = http.createServer(function (req, res) {
function done() {//如果沒(méi)有任何路由規(guī)則匹配的話會(huì)走此函數(shù)
res.end(`Cannot ${req.method} ${req.url}`);
}
//如果路由系統(tǒng)無(wú)法處理榜旦,也就是沒(méi)有一條路由規(guī)則跟請(qǐng)求匹配,是會(huì)把請(qǐng)求交給done
self._router.handle(req, res, done);
});
server.listen(...arguments);
}
module.exports = Application;
掛在了app下各種方法景殷。其中有兩大塊單獨(dú)提出來(lái):一個(gè)是路由溅呢,一個(gè)是路由下的路由對(duì)象,最后一個(gè)是存放的層猿挚。
看router.js
const Route = require('./route');
const Layer = require('./layer');
const url = require('url');
const methods = require('methods');
const init = require('../middle/init');
const slice = Array.prototype.slice;
//let r = Router()
//let r = new Router();
function Router() {
function router(req, res, next) {
router.handle(req, res, next);
}
Object.setPrototypeOf(router, proto);
router.stack = [];
//聲明一個(gè)對(duì)象咐旧,用來(lái)緩存路徑參數(shù)名它對(duì)應(yīng)的回調(diào)函數(shù)數(shù)組
router.paramCallbacks = {};
//在router一加載就會(huì)加載內(nèi)置 中間件
// query
// router.use(init);
return router;
}
let proto = Object.create(null);
//創(chuàng)建一個(gè)Route實(shí)例,向當(dāng)前路由系統(tǒng)中添加一個(gè)層
proto.route = function (path) {
let route = new Route(path);
let layer = new Layer(path, route.dispatch.bind(route));
layer.route = route;
this.stack.push(layer);
return route;
}
proto.use = function (path, handler) {
if (typeof handler != 'function') {
handler = path;
path = '/';
}
let layer = new Layer(path, handler);
layer.route = undefined;//我們正是通過(guò)layer有沒(méi)有route來(lái)判斷是一個(gè)中間件函數(shù)還是一個(gè)路由
this.stack.push(layer);
}
methods.forEach(function (method) {
proto[method] = function (path) {
let route = this.route(path);//是在往Router里添一層
route[method].apply(route, slice.call(arguments, 1));
return this;
}
});
proto.param = function (name, handler) {
if (!this.paramCallbacks[name]) {
this.paramCallbacks[name] = [];
}
// {uid:[handle1,hander2]}
this.paramCallbacks[name].push(handler);
}
/**
* 1.處理中間件
* 2. 處理子路由容器
*/
proto.handle = function (req, res, out) {
//slashAdded是否添加過(guò)/ removed指的是被移除的字符串
let idx = 0, self = this, slashAdded = false, removed = '';
// /user/2
let { pathname } = url.parse(req.url, true);
function next(err) {
if (removed.length > 0) {
req.url = removed + req.url;
removed = '';
}
if (idx >= self.stack.length) {
return out(err);
}
let layer = self.stack[idx++];
//在此匹配路徑 params 正則+url= req.params
if (layer.match(pathname)) {// layer.params
if (!layer.route) { //這一層是中間件層// /user/2
removed = layer.path;// /user
req.url = req.url.slice(removed.length);// /2
if (err) {
layer.handle_error(err, req, res, next);
} else {
layer.handle_request(req, res, next);
}
} else {
if (layer.route && layer.route.handle_method(req.method) && !err) {
//把layer的parmas屬性拷貝給req.params
req.params = layer.params;
self.process_params(layer, req, res, () => {
layer.handle_request(req, res, next);
});
} else {
next(err);
}
}
} else {
next(err);
}
}
next();
}
//用來(lái)處理param參數(shù),處理完成后會(huì)走out函數(shù)
proto.process_params = function (layer, req, res, out) {
let keys = layer.keys;
let self = this;
//用來(lái)處理路徑參數(shù)
let paramIndex = 0 /**key索引**/, key/**key對(duì)象**/, name/**key的值**/, val, callbacks, callback;
//調(diào)用一次param意味著處理一個(gè)路徑參數(shù)
function param() {
if (paramIndex >= keys.length) {
return out();
}
key = keys[paramIndex++];//先取出當(dāng)前的key
name = key.name;// uid
val = layer.params[name];
callbacks = self.paramCallbacks[name];// 取出等待執(zhí)行的回調(diào)函數(shù)數(shù)組
if (!val || !callbacks) {//如果當(dāng)前的key沒(méi)有值亭饵,或者沒(méi)有對(duì)應(yīng)的回調(diào)就直接處理下一個(gè)key
return param();
}
execCallback();
}
let callbackIndex = 0;
function execCallback() {
callback = callbacks[callbackIndex++];
if (!callback) {
return param();//如果此key已經(jīng)沒(méi)有回調(diào)等待執(zhí)行休偶,則意味本key處理完畢,該執(zhí)行一下key
}
callback(req, res, execCallback, val, name);
}
param();
}
module.exports = Router;
/**
* Router
* stack
* layer
* path route
* method handler
* Layer
* Router Layer 路徑 處理函數(shù)(route.dispatch) 有一個(gè)特殊的route屬性
* Route layer 路徑 處理函數(shù)(真正的業(yè)務(wù)代碼) 有一特殊的屬性method
*/
/**
* 1.params param
2.模板引擎 我們會(huì)自己寫(xiě)一個(gè)模板支持辜羊,邏輯判斷 踏兜。if while do
*/
route.js
const Layer = require('./layer');
const methods = require('methods');
const slice = Array.prototype.slice;
function Route(path) {
this.path = path;
this.stack = [];
//表示此路由有有此方法的處理函數(shù)
this.methods = {};
}
Route.prototype.handle_method = function (method) {
method = method.toLowerCase();
return this.methods[method];
}
methods.forEach(function (method) {
Route.prototype[method] = function () {
let handlers = slice.call(arguments);
this.methods[method] = true;
for (let i = 0; i < handlers.length; i++) {
let layer = new Layer('/', handlers[i]);
layer.method = method;
this.stack.push(layer);
}
return this;
}
});
Route.prototype.dispatch = function (req, res, out) {
let idx = 0, self = this;
function next(err) {
if (err) {//如果一旦在路由函數(shù)中出錯(cuò)了,則會(huì)跳過(guò)當(dāng)前路由
return out(err);
}
if (idx >= self.stack.length) {
return out();//route.dispath里的out剛好是Router的next
}
let layer = self.stack[idx++];
if (layer.method == req.method.toLowerCase()) {
layer.handle_request(req, res, next);
} else {
next();
}
}
next();
}
module.exports = Route;
layer.js
const pathToRegexp = require('path-to-regexp');
function Layer(path, handler) {
this.path = path;
this.handler = handler;
this.keys = [];
// this.path =/user/:uid this.keys = [{name:'uid'}];
this.regexp = pathToRegexp(this.path, this.keys);
// /user/:uid /^\/user\/([^\/]+?)(?:\/)?$/i
// /user2/:uid/:name /^\/user2\/([^\/]+?)\/([^\/]+?)(?:\/)?$/i
// / /^\/(?:\/)?$/i
}
//判斷這一層和傳入的路徑是否匹配
Layer.prototype.match = function (path) {
if (this.path == path) {
return true;
}
if (!this.route) {//這一層是一個(gè)中間件層 /user/2
// this.path = /user
return path.startsWith(this.path + '/');
}
//如果這個(gè)Layer是一個(gè)路由的 Layer
if (this.route) {
let matches = this.regexp.exec(path); // /user/1
// ['',1,'zfpx']
if (matches) {
this.params = {};
for (let i = 1; i < matches.length; i++) {
let name = this.keys[i - 1].name;
let val = matches[i];
this.params[name] = val;
}
return true;
}
}
return false;
}
Layer.prototype.handle_request = function (req, res, next, dd) {
this.handler(req, res, next);
}
Layer.prototype.handle_error = function (err, req, res, next) {
if (this.handler.length != 4) {
return next(err);
}
this.handler(err, req, res, next);
}
module.exports = Layer;