Как устроен Chain of Responsibility на примере middleware?
Поведенческие паттерны
Chain of Responsibility передаёт запрос по цепочке обработчиков: каждый либо обрабатывает его и обрывает цепь, либо передаёт дальше. Middleware в Express и Koa — самая известная реализация, на клиенте — интерсепторы axios и ky.
Классика и middleware
- GoF: звено хранит ссылку на следующее и решает, обработать или передать (уровни техподдержки, всплытие событий в DOM).
- Middleware: звено работает до и после вызова
next()и может прервать цепь, не вызвав его (auth вернул 401). - Koa — «луковица»: после
await next()видно результат всего нижележащего стека.
// звено: (req, next) => Promise<Response>
const compose = (chain, send) => (req) => {
const run = (i, r) => (i < chain.length ? chain[i](r, (nr) => run(i + 1, nr)) : send(r));
return run(0, req);
};
let token = 'old';
const api = compose([
async (req, next) => { const res = await next(req); console.log(res.status); return res; },
async (req, next) => {
const res = await next(req);
if (res.status !== 401) return res;
token = await refreshToken(); // свой запрос за новым токеном
return next(req); // осознанный повтор
},
(req, next) => { req.headers.set('Authorization', `Bearer ${token}`); return next(req); },
], fetch);
await api(new Request('/api/me')); // 401 → refresh → 200Подводные камни
- Порядок решает: обработчик ошибок и логгер — первыми, auth — до роутов.
- Забытый
next()вешает запрос; повторный вызов выполняет хвост дважды (koa-compose бросает «next() called multiple times»). - Нужен обработчик по умолчанию (404) на случай, если никто не ответил.
Коротко для собеседования
Chain of Responsibility пропускает запрос через цепочку звеньев, и каждое решает: обработать, изменить или передать дальше. В middleware главное — порядок подключения и ровно один вызов next(), а в конце цепи нужен обработчик по умолчанию вроде 404.