You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

express-validator如何阻止函数直接调用?自定义同类中间件实现问询

How Express-Validator's "Deferred Execution" Works (And How to Build Your Own)

Great question! This is a super clever pattern that express-validator uses, and it's all about deferring validation logic until the request actually hits your server, instead of running it when your app starts up. Let's break this down step by step, then build a simplified version of this pattern ourselves.

The Core Idea Behind Express-Validator's Magic

When you write body('username').isEmail(), you're not actually running an email check right away. Here's what's really happening:

  1. body('username') creates a "validation chain" object: This object keeps track of which field you're targeting (username) and starts with an empty list of validation rules.
  2. isEmail() adds a rule to the chain: Instead of executing validation, this method just adds a function (the actual email-checking logic) to the chain's rule list. It returns the same chain object, which is why you can chain multiple methods like body('password').isLength(...).isAlphanumeric().
  3. Express converts the chain to a middleware function: When you pass the chain to app.post(), express recognizes it as a validation chain and converts it into a standard express middleware (a function with (req, res, next) parameters). This middleware waits until a request comes in, then runs all the collected rules against the request's body, stores errors (if any), and calls next().

In short: You're building a list of rules first, then executing them later when the request arrives.

Building Your Own Version of This Pattern

Let's create a simple custom validation library that works exactly like express-validator. We'll make it support checking field length and requiring a substring.

Step 1: Create the Validation Chain Class

This class will handle collecting rules and acting as an express middleware:

class CustomValidationChain {
  constructor(fieldName) {
    this.fieldName = fieldName;
    this.rules = [];

    // Turn this instance into an express middleware function
    const middleware = (req, res, next) => {
      const errors = [];
      // Run all collected rules against the request body
      this.rules.forEach(rule => {
        const errorMessage = rule(req);
        if (errorMessage) {
          errors.push(errorMessage);
        }
      });
      // Store errors on the request for later use
      req.customValidationErrors = errors;
      next();
    };

    // Attach our rule methods to the middleware function
    // This lets us chain calls like .isLength() or .includes()
    middleware.isLength = this.isLength.bind(this);
    middleware.includes = this.includes.bind(this);

    return middleware;
  }

  // Add a rule to check field length
  isLength({ min, max }) {
    this.rules.push((req) => {
      const value = req.body[this.fieldName];
      if (!value || value.length < min || value.length > max) {
        return `${this.fieldName} must be between ${min} and ${max} characters`;
      }
      return null; // No error
    });
    return this; // Return the middleware to keep chaining
  }

  // Add a rule to check for a required substring
  includes(substring) {
    this.rules.push((req) => {
      const value = req.body[this.fieldName];
      if (!value || !value.includes(substring)) {
        return `${this.fieldName} must contain the text "${substring}"`;
      }
      return null; // No error
    });
    return this; // Keep chaining
  }
}

// Mimic express-validator's `body()` function
const body = (fieldName) => {
  return new CustomValidationChain(fieldName);
};

Step 2: Use Your Custom Middleware

Now you can use it exactly like express-validator in your routes:

const express = require('express');
const app = express();
app.use(express.json());

app.post('/signup',
  // Chain validation rules
  body('username').isLength({ min: 3, max: 15 }).includes('user'),
  body('password').isLength({ min: 6, max: 20 }),
  (req, res) => {
    // Check for validation errors
    if (req.customValidationErrors.length > 0) {
      return res.status(400).json({ errors: req.customValidationErrors });
    }

    // If no errors, proceed to create the user
    res.json({ message: 'Signup successful!' });
  }
);

app.listen(3000);

Key Takeaways

  • Deferred execution: Instead of running logic immediately, you collect rules first and run them later when the request arrives.
  • Chaining: By returning the same object/function from each rule method, you enable clean, readable chained calls.
  • Middleware compatibility: The final object/function must conform to express's middleware signature ((req, res, next)) so it can be used in routes.

This pattern isn't just for validators either—you can use it for any middleware where you need to configure behavior with chained methods before executing logic at request time.

内容的提问来源于stack exchange,提问作者Glenn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 14:07:31