阿里云主机折上折
  • 微信号
Current Site:Index > The basic concept and working principle of middleware

The basic concept and working principle of middleware

Author:Chuan Chen 阅读数:52372人阅读 分类: Node.js

Middleware is a core concept in the Express framework, used to insert custom logic during the processing of HTTP requests and responses. It enables preprocessing, post-processing, or interception of requests through chained calls, providing flexible extensibility for web applications.

Definition and Role of Middleware

Middleware is essentially a function with a specific signature, accepting three parameters: req (request object), res (response object), and next (callback function). Its typical structure is as follows:

function middleware(req, res, next) {
  // Processing logic
  next(); // Pass control to the next middleware
}

Its primary roles include:

  1. Modifying request or response objects (e.g., adding headers)
  2. Terminating the request-response cycle (e.g., returning 403 on failed authorization)
  3. Invoking the next middleware (via next())

Workflow of Middleware

The request-handling process in an Express application can be viewed as a middleware pipeline. When an HTTP request arrives, it is executed sequentially in the order the middleware is registered:

  1. The request enters the first middleware
  2. After processing, the middleware calls next()
  3. Control is transferred to the next middleware
  4. The process repeats until the response is sent

Example workflow:

app.use((req, res, next) => {
  console.log('First middleware');
  next();
});

app.use((req, res, next) => {
  console.log('Second middleware');
  next();
});

app.get('/', (req, res) => {
  res.send('Hello World');
});

When requesting /, the output will sequentially display "First middleware" and "Second middleware."

Types of Middleware

Application-Level Middleware

Bound to the application instance via app.use() or app.METHOD():

// Applies to all requests
app.use((req, res, next) => {
  console.log('Time:', Date.now());
  next();
});

// Applies only to GET /user
app.get('/user', (req, res, next) => {
  res.send('USER');
});

Router-Level Middleware

Acts on specific route paths:

const userRouter = express.Router();
userRouter.use((req, res, next) => {
  console.log('User route middleware');
  next();
});
app.use('/users', userRouter);

Error-Handling Middleware

Accepts four parameters (err, req, res, next):

app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

Built-in Middleware

Out-of-the-box middleware provided by Express:

  • express.json(): Parses JSON request bodies
  • express.urlencoded(): Parses URL-encoded request bodies
  • express.static(): Serves static files
app.use(express.json());
app.use(express.static('public'));

Third-Party Middleware

Community-provided extensions:

const morgan = require('morgan');
app.use(morgan('dev'));  // Logging

Execution Order of Middleware

The execution order of middleware strictly depends on the registration order. The following example demonstrates the importance of order:

// This middleware will never execute
app.use((req, res, next) => {
  console.log('This will never run');
  next();
});

// Because the previous middleware terminates the flow
app.use((req, res, next) => {
  res.send('Early response');
});

// Correct order should be:
app.use((req, res, next) => {
  const isValid = checkAuth(req);
  if (!isValid) return res.status(401).end();
  next();
});

app.get('/protected', (req, res) => {
  res.send('Protected content');
});

Composition and Modularization of Middleware

Multiple middleware functions can be combined:

function logOriginalUrl(req, res, next) {
  console.log('Request URL:', req.originalUrl);
  next();
}

function logMethod(req, res, next) {
  console.log('Request Type:', req.method);
  next();
}

app.get('/user', [logOriginalUrl, logMethod], (req, res) => {
  res.send('User Info');
});

Example of modular organization:

// logger.js
module.exports = function(req, res, next) {
  console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
  next();
};

// app.js
const logger = require('./logger');
app.use(logger);

Error-Handling Mechanism of Middleware

Error-handling middleware requires special definition:

// Synchronous errors are automatically caught
app.get('/error', (req, res) => {
  throw new Error('BROKEN');
});

// Asynchronous errors must be explicitly passed
app.get('/async-error', (req, res, next) => {
  fs.readFile('/nonexistent', (err, data) => {
    if (err) next(err);  // Must manually pass the error
    else res.send(data);
  });
});

// Error-handling middleware
app.use((err, req, res, next) => {
  res.status(500).json({ error: err.message });
});

Performance Optimization of Middleware

  1. Filter unnecessary middleware execution:
app.use('/api', (req, res, next) => {
  // Only applies to /api paths
  next();
});
  1. Use conditional middleware:
const isAdmin = (req, res, next) => {
  if (req.user.role === 'admin') return next();
  res.status(403).end();
};

app.get('/admin', isAdmin, (req, res) => {
  res.send('Admin panel');
});
  1. Middleware caching:
const cache = {};

app.use('/data', (req, res, next) => {
  const key = req.originalUrl;
  if (cache[key]) return res.json(cache[key]);
  
  // Simulate time-consuming operation
  setTimeout(() => {
    const data = { value: Math.random() };
    cache[key] = data;
    res.json(data);
  }, 1000);
});

Practical Use Cases of Middleware

Authentication

const authenticate = (req, res, next) => {
  const token = req.headers.authorization;
  if (!token) return res.status(401).send('Unauthorized');
  
  try {
    req.user = verifyToken(token);
    next();
  } catch (err) {
    res.status(403).send('Invalid token');
  }
};

app.get('/profile', authenticate, (req, res) => {
  res.json(req.user);
});

Request Data Processing

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.post('/submit', (req, res) => {
  console.log(req.body);  // Automatically parsed request body
  res.send('Data received');
});

Response Time Monitoring

app.use((req, res, next) => {
  const start = Date.now();
  res.on('finish', () => {
    const duration = Date.now() - start;
    console.log(`${req.method} ${req.url} - ${duration}ms`);
  });
  next();
});

CORS Handling

app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE');
  res.header('Access-Control-Allow-Headers', 'Content-Type,Authorization');
  next();
});

Advanced Middleware Patterns

Middleware Factory

A function that returns middleware:

function createLogger(format) {
  return function(req, res, next) {
    console.log(`[${format}] ${req.method} ${req.url}`);
    next();
  };
}

app.use(createLogger('short'));

Configurable Middleware

function configurableMiddleware(options = {}) {
  return function(req, res, next) {
    if (options.logRequest) {
      console.log(`${req.method} ${req.url}`);
    }
    if (options.addTimestamp) {
      req.timestamp = Date.now();
    }
    next();
  };
}

app.use(configurableMiddleware({
  logRequest: true,
  addTimestamp: true
}));

Middleware Composition Tool

const compose = require('koa-compose'); // Can also be self-implemented

const middleware1 = (req, res, next) => {
  console.log('Middleware 1');
  next();
};

const middleware2 = (req, res, next) => {
  console.log('Middleware 2');
  next();
};

app.use(compose([middleware1, middleware2]));

Testing Middleware

Using supertest to test middleware:

const request = require('supertest');
const app = require('../app');

describe('Auth Middleware', () => {
  it('should block unauthorized access', async () => {
    const res = await request(app)
      .get('/protected')
      .expect(401);
  });

  it('should allow authorized access', async () => {
    const res = await request(app)
      .get('/protected')
      .set('Authorization', 'valid-token')
      .expect(200);
  });
});

Debugging Techniques for Middleware

  1. Use debugging placeholders:
app.use((req, res, next) => {
  console.log('Request reached middleware');
  next();
  console.log('Request left middleware');
});
  1. Inspect the middleware stack:
app.use((req, res, next) => {
  console.log('Current stack:', this._router.stack);
  next();
});
  1. Use debugging tools:
const debug = require('debug')('app:middleware');
app.use((req, res, next) => {
  debug('Request headers:', req.headers);
  next();
});

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.