PassFront
  1. PassFront
  2. База знаний
  3. Поведенческие паттерны

Как устроен 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.

Другие вопросы раздела