题目 实现一个简单的中间件系统 描述 你是否使用过 Express Middleware ?
Middleware函数是指可以串联起来的,有特定interface的函数。
app.use('/user/:id', function (req, res, next) {
next()
}, function (req, res, next) {
next(new Error('sth wrong'))
})
请实现一个简单的middleware 系统!
type Request = object
type NextFunc = (error?: any) => void
type MiddlewareFunc =
(req: Request, next: NextFunc) => void
type ErrorHandler =
(error: Error, req: Request, next: NextFunc) => void
class Middleware {
use(func: MiddlewareFunc | ErrorHandler) {
// do any async operations
// call next() to trigger next function
}
start(req: Request) {
// trigger all functions with a req object
}
}
有了上述之后,我们可以支持类似Express的用法。
const middleware = new Middleware()
middleware.use((req, next) => {
req.a = 1
next()
})
middleware.use((req, next) => {
req.b = 2
next()
})
middleware.use((req, next) => {
console.log(req)
})
middleware.start({})
// {a: 1, b: 2}
注意到 use()
支持ErrorHandler,ErrorHandler有3个参数,在发生Error或者next()
被含参数调用的时候触发,比如这样。
const middleware = new Middleware()
// throw an error at first function
middleware.use((req, next) => {
req.a = 1
throw new Error('sth wrong')
// or `next(new Error('sth wrong'))`
})
// since error occurs, this is skipped
middleware.use((req, next) => {
req.b = 2
})
// since error occurs, this is skipped
middleware.use((req, next) => {
console.log(req)
})
// since error occurs, this is called
middleware.use((error, req, next) => {
console.log(error)
console.log(req)
})
middleware.start({})
// Error: sth wrong
// {a: 1}
答案 第一时间有点思路但没写出来,看了评论的解答
核心就是
- 注意判断是处理错误的还是常规的(通过func.length判断)
- try catch,next参数判断是否抛出错误,且要做错误处理
实现1
通过两个数组分清常规处理func,与错误处理func,分别维护对于索引,思路清晰
class Middleware {
/**
* @param {MiddlewareFunc} func
*/
constructor() {
this.callbacks = [];
this.errorCallbacks = [];
}
use(func) {
if (func.length == 2) {
this.callbacks.push(func);
} else {
this.errorCallbacks.push(func);
}
}
/**
* @param {Request} req
*/
start(req) {
var i = 0;
var req = req;
var ei = 0;
const self = this;
function next(error) {
var func;
var args = [req, next]
if (error) {
func = self.errorCallbacks.shift();
args = [error, ...args]
} else {
func = self.callbacks.shift();
}
try {
func.apply(undefined, args);
} catch (error) {
next(error);
}
}
next();
}
}
实现2
一个队列实现
//摘自评论区: https://bigfrontend.dev/problem/create-a-middleware-system/discuss
class Middleware {
/**
* @param {MiddlewareFunc} func
*/
constructor() {
// Create a function queue to help with execution
this.funcs = [];
this.req = null;
}
use(func) {
// Push the function into Queue
this.funcs.push(func);
}
/**
* @param {Request} req
*/
start(req) {
this.req = req;
// Start the chain
this.next();
}
next = (err) => {
// take out the function to execute from the queue
const toExecute = this.funcs.shift();
// Catch execution error when a function throw an error
try {
// args length tells us if its a normal call or an error call
if (toExecute.length === 2) {
// there is no error, execute the function with current request and next()
if (!err) {
toExecute(this.req, this.next);
}
// There is an error, call next() immediately for error handling. This will now keep on dequeuing funs queue
// till we get an error handler function with 3 args
else{
this.next(err);
}
}
// There's an error and now we got a func with more than 2 args, i.e. an error handler
else {
toExecute(err, this.req, this.next);
}
}
catch (e) {
this.next(e);
}
}
}