Node.js 路由请求方式大全解:深度剖析与工程实践

文章目录

  • [🌐 Node.js 路由请求方式大全解:深度剖析与工程实践](#🌐 Node.js 路由请求方式大全解:深度剖析与工程实践)
    • [一、📜 HTTP 请求方法全景图](#一、📜 HTTP 请求方法全景图)
      • [🏆 核心方法深度对比](#🏆 核心方法深度对比)
      • [HTTP 请求方法概念对比表](#HTTP 请求方法概念对比表)
      • [🛠️ 特殊方法应用场景](#🛠️ 特殊方法应用场景)
    • [二、🎨 各方法深度解析](#二、🎨 各方法深度解析)
    • [三、🔄 请求-响应生命周期(工业级实现)](#三、🔄 请求-响应生命周期(工业级实现))
    • [四、🌈 方法选择决策树(业务场景导向)](#四、🌈 方法选择决策树(业务场景导向))
    • [五、💎 专业实践建议(生产环境级)](#五、💎 专业实践建议(生产环境级))
      • [1. 高级幂等性控制](#1. 高级幂等性控制)
      • [2. 批量操作设计模式](#2. 批量操作设计模式)
      • [3. 内容协商进阶实现](#3. 内容协商进阶实现)
      • [4. 超媒体控制 (HATEOAS)](#4. 超媒体控制 (HATEOAS))
    • [六、🚀 性能优化专项](#六、🚀 性能优化专项)
      • [1. 条件请求深度优化](#1. 条件请求深度优化)
      • [2. 流式响应处理](#2. 流式响应处理)
    • [七、🔒 安全加固方案](#七、🔒 安全加固方案)
      • [1. 方法过滤中间件](#1. 方法过滤中间件)
      • [2. CORS 精细控制](#2. CORS 精细控制)

本文全面解析HTTP请求方法在Node.js路由中的应用,重点对比了7种核心方法:

GET - 安全幂等的读取操作,适合数据检索,可缓存优化性能

HEAD - 仅获取头部信息,用于资源检查,网络开销极小

POST - 非幂等的创建操作,适合表单提交和复杂业务

PUT - 幂等的全量更新,由客户端控制资源ID

PATCH - 部分更新,采用JSON Patch可保持幂等性

DELETE - 幂等的资源删除,建议异步实现

OPTIONS - 支持CORS预检和API自描述

文章通过对比表格详细分析了各方法的语义特性、安全幂等性、缓存能力及典型应用场景,并提供了Express框架中的代码实现示例,特别强调了GET缓存优化、POST批量操作等工程实践建议。

🌐 Node.js 路由请求方式大全解:深度剖析与工程实践

HTTP 协议是 Web 开发的基石,而 Express 框架提供的路由方法则是构建 RESTful API 的核心工具。本文将全面解析各种 HTTP 方法,从基础概念到高级应用,从规范要求到工程实践。

一、📜 HTTP 请求方法全景图

🏆 核心方法深度对比

以下是主要 HTTP 请求方法的性能对比表,包含关键指标和适用场景分析:

方法 安全性 幂等性 可缓存性 网络传输量 典型延迟 适用场景 性能优化建议
GET ✔️ ✔️ ✔️ 数据检索、资源读取 1. 启用缓存 2. 使用条件请求
HEAD ✔️ ✔️ ✔️ 极小 最低 检查资源是否存在/元数据 替代GET获取头部信息
POST ✖️ ✖️ ✖️ 中-大 中-高 创建资源、非幂等操作 1. 批量操作 2. 压缩请求体
PUT ✖️ ✔️ ✖️ 完整资源更新 1. 小资源更新 2. 断点续传
PATCH ✖️ △* ✖️ 小-中 部分资源更新 1. JSON Patch格式 2. 增量更新
DELETE ✖️ ✔️ ✖️ 删除资源 1. 异步删除 2. 软删除
OPTIONS ✔️ ✔️ ✔️ 极小 CORS预检、方法探测 缓存预检结果

HTTP 请求方法概念对比表

方法 语义 安全性 幂等性 可缓存性 请求体 响应体 典型状态码 主要应用场景
GET 获取资源 ✔️ 安全 ✔️ 幂等 ✔️ 可缓存 ❌ 不应有 ✔️ 包含 200 OK, 304 Not Modified 数据检索、资源读取
HEAD 获取资源头信息 ✔️ 安全 ✔️ 幂等 ✔️ 可缓存 ❌ 不应有 ❌ 不包含 200 OK, 304 检查资源是否存在、验证有效性
POST 创建资源/执行操作 ❌ 不安全 ❌ 非幂等 ❌ 不可缓存 ✔️ 包含 ✔️ 包含 201 Created, 200 OK 表单提交、创建资源、触发操作
PUT 完整替换资源 ❌ 不安全 ✔️ 幂等 ❌ 不可缓存 ✔️ 包含 ✔️ 可选 200 OK, 204 No Content 全量更新资源(客户端控制ID)
PATCH 部分更新资源 ❌ 不安全 △ 视实现* ❌ 不可缓存 ✔️ 包含 ✔️ 包含 200 OK, 204 增量更新、字段级修改
DELETE 删除资源 ❌ 不安全 ✔️ 幂等 ❌ 不可缓存 ❌ 可选 ✔️ 可选 200 OK, 204, 202 Accepted 删除指定资源
OPTIONS 获取支持的通信选项 ✔️ 安全 ✔️ 幂等 ✔️ 可缓存 ❌ 不应有 ✔️ 包含 200 OK CORS预检、API能力探测

注:PATCH的幂等性取决于实现方式(如使用JSON Patch标准则幂等)

🛠️ 特殊方法应用场景

方法 网络开销 性能影响 安全风险 适用场景 浏览器支持
HEAD 极小 大文件下载前检查 完全
OPTIONS 中等 CORS 预检、API 自描述 完全
CONNECT HTTPS 代理隧道 受限
TRACE 诊断循环攻击、请求链跟踪 禁用

二、🎨 各方法深度解析

1. GET - 数据查看器(性能优化方向)

GET 是 HTTP 协议中最基础、最常用的方法,专门用于安全地获取数据而不会修改服务器资源。以下是 GET 方法的全面解析和最佳实践。

核心特性

GET 方法的三个关键特性:

  1. 安全性 - 不应修改服务器状态(只读操作)
  2. 幂等性 - 多次执行相同请求结果一致
  3. 可缓存 - 响应可被浏览器和代理服务器缓存
主要使用场景

1. 获取资源集合

  • 场景:获取列表数据(分页/过滤/排序)

  • 示例

    javascript 复制代码
    // 获取用户列表(带分页和过滤)
    router.get('/users', (req, res) => {
      const { page = 1, limit = 10, role } = req.query;
      
      const filter = {};
      if (role) filter.role = role;
      
      User.find(filter)
        .skip((page - 1) * limit)
        .limit(Number(limit))
        .then(users => res.json({
          data: users,
          page,
          total: await User.countDocuments(filter)
        }))
        .catch(err => res.status(500).json(err));
    });

2. 获取单个资源

  • 场景:通过ID获取详情

  • 示例

    javascript 复制代码
    // 获取特定用户详情
    router.get('/users/:id', (req, res) => {
      User.findById(req.params.id)
        .then(user => user ? res.json(user) : res.sendStatus(404))
        .catch(err => res.status(500).json(err));
    });

3. 搜索和查询

  • 场景:使用查询参数实现复杂查询

  • 示例

    javascript 复制代码
    // 产品搜索接口
    router.get('/products/search', (req, res) => {
      const { q, minPrice, maxPrice, category } = req.query;
      const query = {};
      
      if (q) query.$text = { $search: q };
      if (category) query.category = category;
      if (minPrice || maxPrice) {
        query.price = {};
        if (minPrice) query.price.$gte = Number(minPrice);
        if (maxPrice) query.price.$lte = Number(maxPrice);
      }
      
      Product.find(query)
        .then(products => res.json(products))
        .catch(err => res.status(500).json(err));
    });

4. 获取衍生数据

  • 场景:统计、报表、聚合数据

  • 示例

    javascript 复制代码
    // 获取销售统计数据
    router.get('/sales/stats', (req, res) => {
      const { startDate, endDate } = req.query;
      
      Order.aggregate([
        { $match: { 
          date: { 
            $gte: new Date(startDate), 
            $lte: new Date(endDate) 
          } 
        }},
        { $group: {
          _id: null,
          totalSales: { $sum: "$amount" },
          avgOrder: { $avg: "$amount" },
          count: { $sum: 1 }
        }}
      ])
      .then(stats => res.json(stats[0] || {}))
      .catch(err => res.status(500).json(err));
    });

5. 导出数据

  • 场景:CSV/Excel/PDF等格式导出

  • 示例:

    javascript 复制代码
    // 导出用户列表为CSV
    router.get('/users/export', (req, res) => {
      const { format = 'csv' } = req.query;
      
      User.find()
        .then(users => {
          if (format === 'csv') {
            res.setHeader('Content-Type', 'text/csv');
            res.setHeader('Content-Disposition', 'attachment; filename=users.csv');
            return csv().from(users).pipe(res);
          }
          res.json(users);
        })
        .catch(err => res.status(500).json(err));
    });
与 POST 方法的区别
特性 GET POST
安全性 安全(只读) 非安全(可能修改数据)
幂等性
缓存 可缓存 不可缓存
数据位置 URL查询参数 请求体
数据长度 受URL长度限制(约2KB) 无限制
浏览器历史 保留在历史记录 不保留
最佳实践
  1. 响应状态码

    • 成功:200 OK
    • 无内容:204 No Content
    • 重定向:301/302/304
    • 客户端错误:400 Bad Request/404 Not Found
  2. 查询参数设计

    javascript 复制代码
    // 好的设计 - 清晰、可预测
    /api/products?category=electronics&price[lte]=1000&sort=-rating&limit=10
    
    // 避免 - 过于复杂
    /api/products?q=filter:category=electronics,price<=1000;sort:rating:desc;page:1
  3. 性能优化

    • 实现条件请求(ETag/Last-Modified)
    • 支持压缩(Accept-Encoding)
    • 分页大数据集
    javascript 复制代码
    router.get('/articles', (req, res) => {
      // 检查If-None-Match头
      const articles = await Article.find();
      const etag = generateETag(articles);
      if (req.headers['if-none-match'] === etag) {
        return res.sendStatus(304);
      }
      res.set('ETag', etag).json(articles);
    });
  4. 安全性

    • 敏感数据仍需保护(即使GET是安全的)
    • 防止敏感信息出现在URL中
    • 防范XSS攻击(转义输出)
实际案例

案例1:电子商务 - 产品筛选

javascript 复制代码
router.get('/products', cacheMiddleware, async (req, res) => {
  try {
    const { 
      search, 
      minPrice, 
      maxPrice, 
      category, 
      sort = '-createdAt', 
      page = 1, 
      perPage = 20 
    } = req.query;

    const query = {};
    if (search) query.title = { $regex: search, $options: 'i' };
    if (category) query.category = category;
    if (minPrice || maxPrice) {
      query.price = {};
      if (minPrice) query.price.$gte = Number(minPrice);
      if (maxPrice) query.price.$lte = Number(maxPrice);
    }

    const [products, total] = await Promise.all([
      Product.find(query)
        .sort(sort)
        .skip((page - 1) * perPage)
        .limit(Number(perPage)),
      Product.countDocuments(query)
    ]);

    res.json({
      data: products,
      meta: {
        page: Number(page),
        perPage: Number(perPage),
        total,
        lastPage: Math.ceil(total / perPage)
      }
    });
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});

案例2:内容管理 - 条件内容获取

javascript 复制代码
router.get('/content/:slug', async (req, res) => {
  try {
    const { slug } = req.params;
    const { draft = 'false', locale = 'en' } = req.query;
    
    const content = await Content.findOne({
      slug,
      locale,
      ...(draft === 'true' ? {} : { status: 'published' })
    }).lean();

    if (!content) return res.sendStatus(404);
    
    // 客户端缓存验证
    const lastModified = content.updatedAt.toUTCString();
    if (req.headers['if-modified-since'] === lastModified) {
      return res.sendStatus(304);
    }

    res.set('Last-Modified', lastModified)
       .json(content);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

GET 方法是 RESTful API 的基石,专门用于安全地检索数据。正确使用 GET 方法可以创建高效、可缓存且符合语义的 API 接口。对于所有不修改服务器状态的数据请求,GET 都应该是首选方法。

2. POST - 数据创造者(事务安全方向)

POST 是 HTTP 协议中最常用的方法之一,在 RESTful API 设计中有着广泛的应用场景。以下是 POST 方法的全面解析和最佳实践。

核心特性

POST 方法的三个关键特性:

  1. 非幂等性 - 多次执行相同的 POST 请求会产生新的资源
  2. 通用性 - 可用于各种非检索类操作
  3. 灵活性 - 不限制请求体格式和内容
主要使用场景

1. 创建新资源(最典型场景)

  • 场景:在服务器创建新的资源实例

  • 示例

    javascript 复制代码
    // 创建新用户
    router.post('/users', (req, res) => {
      const newUser = new User(req.body);
      newUser.save()
        .then(user => res.status(201).json(user))
        .catch(err => res.status(400).json(err));
    });
  • RESTful 实践

    • 返回 201 Created 状态码
    • 在 Location 头中包含新资源的 URL
    • 返回创建的资源表示

2. 执行非幂等性操作

  • 场景:如支付、提交订单等

  • 示例

    javascript 复制代码
    // 提交订单
    router.post('/orders', (req, res) => {
      OrderService.createOrder(req.body)
        .then(order => res.status(201).json(order))
        .catch(err => res.status(400).json(err));
    });

3. 处理表单提交

  • 场景:HTML 表单提交、文件上传等

  • 示例

    javascript 复制代码
    // 文件上传
    const multer = require('multer');
    const upload = multer({ dest: 'uploads/' });
    
    router.post('/upload', upload.single('file'), (req, res) => {
      res.json({ filename: req.file.filename });
    });

4. 复杂查询(当 GET 不适用时)

  • 场景:查询参数太长或包含敏感信息

  • 示例:

    javascript 复制代码
    // 复杂报表查询
    router.post('/reports', (req, res) => {
      ReportService.generate(req.body.criteria)
        .then(data => res.json(data))
        .catch(err => res.status(400).json(err));
    });

5. 控制器动作(非CRUD操作)

  • 场景:如密码重置、验证等

  • 示例

    javascript 复制代码
    // 密码重置请求
    router.post('/password-reset', (req, res) => {
      AuthService.requestPasswordReset(req.body.email)
        .then(() => res.json({ message: '重置链接已发送' }))
        .catch(err => res.status(400).json(err));
    });
与 PUT 方法的区别
特性 POST PUT
幂等性
资源标识符 服务器生成 客户端指定
主要用途 创建新资源 替换现有资源
响应 201 Created (通常) 200/204 (通常)
缓存 不可缓存 不可缓存
最佳实践
  1. 响应状态码

    • 创建成功:201 Created
    • 处理成功但无资源创建:200 OK
    • 无效请求:400 Bad Request
    • 未授权:401 Unauthorized
    • 禁止访问:403 Forbidden
    • 冲突:409 Conflict
  2. 请求体设计

    • 使用 JSON 作为主要格式
    • 对复杂数据保持扁平结构
    json 复制代码
    // 好例子
    {
      "name": "张三",
      "email": "[email protected]"
    }
    
    // 避免嵌套过深
    {
      "user": {
        "personal": {
          "name": "张三"
        }
      }
    }
  3. 安全性考虑

    • 始终验证和清理输入
    • 敏感操作应要求额外验证
    • 考虑实现速率限制
  4. 批量创建

    javascript 复制代码
    // 批量创建用户
    router.post('/users/batch', (req, res) => {
      User.insertMany(req.body.users)
        .then(users => res.status(201).json(users))
        .catch(err => res.status(400).json(err));
    });
实际案例

案例1:电子商务 - 购物车结算

javascript 复制代码
router.post('/checkout', authMiddleware, async (req, res) => {
  try {
    const order = await CheckoutService.process({
      userId: req.user.id,
      cartId: req.body.cartId,
      shippingInfo: req.body.shipping
    });
    
    res.status(201).json({
      orderId: order.id,
      total: order.total,
      estimatedDelivery: order.estimatedDelivery
    });
  } catch (err) {
    if (err.name === 'ValidationError') {
      res.status(400).json({ error: err.message });
    } else {
      res.status(500).json({ error: '处理订单时出错' });
    }
  }
});

案例2:社交媒体 - 发布动态

javascript 复制代码
router.post('/posts', authMiddleware, upload.array('media', 5), async (req, res) => {
  try {
    const post = await PostService.create({
      author: req.user.id,
      content: req.body.content,
      media: req.files.map(file => ({
        url: file.path,
        type: file.mimetype.split('/')[0] // 'image' 或 'video'
      })),
      privacy: req.body.privacy || 'public'
    });
    
    res.status(201).json(post);
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});

POST 方法是 RESTful API 中最灵活的方法,适用于各种创建和处理场景。正确使用 POST 方法可以使 API 更加符合语义化设计,同时保证数据操作的安全性和可靠性。对于非检索类的操作,当不确定使用哪种方法时,POST 通常是最安全的选择。

3. PUT - 数据替换器(并发控制方向)

PUT 是 HTTP 协议中的一个重要方法,在 RESTful API 设计中有着特定的用途。以下是 PUT 方法的详细使用场景和最佳实践:

核心特性

PUT 方法的两个关键特性:

  1. 幂等性 - 多次执行相同的 PUT 请求会产生相同的结果
  2. 完整替换 - 用请求负载完全替换目标资源
主要使用场景

1. 完整资源更新

  • 场景:当需要更新资源的全部属性时

  • 示例

    javascript 复制代码
    // 更新用户全部信息
    router.put('/users/:id', (req, res) => {
      const { id } = req.params;
      const updatedUser = req.body;
      
      // 用新数据完全替换旧数据
      UserModel.replaceById(id, updatedUser)
        .then(() => res.sendStatus(204))
        .catch(err => res.status(500).send(err));
    });

2. 资源创建(已知ID)

  • 场景:当客户端知道要创建资源的标识符时

  • 示例

    javascript 复制代码
    // 创建指定ID的文档
    router.put('/documents/:docId', (req, res) => {
      const { docId } = req.params;
      const newDoc = req.body;
      
      // 如果不存在则创建,存在则替换
      DocumentModel.updateOrCreate(docId, newDoc)
        .then(() => res.status(201).json(newDoc))
        .catch(err => res.status(500).send(err));
    });

3. 文件上传

  • 场景:上传文件到指定位置

  • 示例

    javascript 复制代码
    // 上传文件到指定路径
    router.put('/files/:filename', uploadMiddleware, (req, res) => {
      // 文件已保存到指定位置
      res.sendStatus(200);
    });
与 PATCH 的区别
特性 PUT PATCH
语义 完全替换 部分更新
幂等性 不一定
请求体 完整资源 仅包含要修改的字段
资源存在性 不存在时可创建 必须已存在

示例对比

javascript 复制代码
// PUT 示例 - 替换整个用户资源
PUT /users/123
Body: { "name": "张三", "email": "[email protected]", "age": 30 }

// PATCH 示例 - 只更新邮箱
PATCH /users/123
Body: { "email": "[email protected]" }
最佳实践
  1. 明确语义

    • 使用 PUT 时应该传递完整的资源表示
    • 客户端应该先 GET 资源,修改后再 PUT 回去
  2. 响应状态码

    • 创建成功:201 Created
    • 更新成功:200 OK204 No Content
    • 无效请求:400 Bad Request
    • 未授权:401 Unauthorized
    • 资源不存在:404 Not Found
  3. 并发控制

    • 使用 ETag 或 Last-Modified 头处理并发更新

      router.put('/users/:id', (req, res) => {
      const ifMatch = req.get('If-Match');
      // 验证ETag是否匹配
      });

  4. 安全性

    • PUT 操作应该有权限验证
    • 敏感字段应该在服务器端处理,不应依赖客户端提供
实际案例

案例1:CMS 文章更新

javascript 复制代码
// 更新整篇文章
router.put('/articles/:id', authMiddleware, (req, res) => {
  const { id } = req.params;
  const articleData = req.body;
  
  if (!isValidArticle(articleData)) {
    return res.status(400).json({ error: "Invalid article data" });
  }

  Article.findByIdAndUpdate(id, articleData, { new: true, overwrite: true })
    .then(updated => {
      if (!updated) return res.sendStatus(404);
      res.json(updated);
    })
    .catch(err => res.status(500).send(err));
});

案例2:配置信息存储

javascript 复制代码
// 存储系统配置
router.put('/config/:key', adminOnly, (req, res) => {
  const { key } = req.params;
  const configValue = req.body.value;
  
  ConfigStore.upsert(key, configValue)
    .then(() => res.sendStatus(204))
    .catch(err => res.status(500).send(err));
});

PUT 方法在需要完整替换资源或已知资源标识符的创建场景中非常有用,正确使用可以使 API 更加符合 RESTful 设计原则。

4. DELETE - 数据清除者(业务状态方向)

DELETE 是 HTTP 协议中用于删除资源的标准方法,在 RESTful API 设计中扮演着重要角色。以下是 DELETE 方法的详细使用场景和最佳实践。

核心特性

DELETE 方法的两个关键特性:

  1. 幂等性 - 多次执行相同的 DELETE 请求会产生相同的结果(第一次删除后资源就不存在了)
  2. 资源删除 - 从服务器移除指定的资源
主要使用场景

1. 物理删除资源

  • 场景:从数据库或存储中永久移除资源

  • 示例

    javascript 复制代码
    // 删除用户账户
    router.delete('/users/:id', authMiddleware, (req, res) => {
      const { id } = req.params;
      
      User.findByIdAndDelete(id)
        .then(deletedUser => {
          if (!deletedUser) return res.status(404).json({ error: "用户不存在" });
          res.sendStatus(204); // 成功删除,无内容返回
        })
        .catch(err => res.status(500).json({ error: err.message }));
    });

2. 逻辑删除(软删除)

  • 场景:通过标记字段实现"删除"效果而不实际删除数据

  • 示例

    javascript 复制代码
    // 软删除文章(标记为已删除)
    router.delete('/articles/:id', (req, res) => {
      Article.findByIdAndUpdate(
        req.params.id,
        { deleted: true, deletedAt: new Date() },
        { new: true }
      )
      .then(article => res.json(article))
      .catch(err => res.status(500).json(err));
    });

3. 取消关联关系

  • 场景:移除资源间的关联关系而非删除资源本身

  • 示例

    javascript 复制代码
    // 从群组中移除用户
    router.delete('/groups/:groupId/members/:userId', (req, res) => {
      Group.updateOne(
        { _id: req.params.groupId },
        { $pull: { members: req.params.userId } }
      )
      .then(() => res.sendStatus(204))
      .catch(err => res.status(500).json(err));
    });
与 POST 删除的区别
特性 DELETE POST (模拟删除)
语义 标准删除方法 非标准,需要自定义
幂等性 取决于实现
RESTful 符合 不符合
缓存 可被缓存 通常不被缓存

不推荐的做法

javascript 复制代码
// 不推荐使用POST来删除资源
router.post('/users/:id/delete', (req, res) => {
  // 删除逻辑
});
最佳实践
  1. 响应状态码

    • 删除成功:204 No Content(推荐)或 200 OK
    • 资源不存在:404 Not Found
    • 未授权:401 Unauthorized
    • 禁止访问:403 Forbidden
  2. 安全性考虑

    • 必须实施权限验证
    • 敏感删除操作应该要求二次确认
    • 考虑添加删除原因记录
  3. 批量删除

    javascript 复制代码
    // 批量删除符合条件的数据
    router.delete('/products', (req, res) => {
      const { category } = req.query;
      
      Product.deleteMany({ category })
        .then(result => res.json({ deletedCount: result.deletedCount }))
        .catch(err => res.status(500).json(err));
    });
  4. 级联删除处理

    javascript 复制代码
    // 删除用户及其关联数据
    router.delete('/users/:id', async (req, res) => {
      try {
        await mongoose.connection.transaction(async session => {
          await Post.deleteMany({ author: req.params.id }).session(session);
          await Comment.deleteMany({ user: req.params.id }).session(session);
          await User.findByIdAndDelete(req.params.id).session(session);
        });
        res.sendStatus(204);
      } catch (err) {
        res.status(500).json(err);
      }
    });
实际案例

案例1:电商平台商品删除

javascript 复制代码
router.delete('/products/:id', adminAuth, async (req, res) => {
  try {
    // 检查商品是否存在订单中
    const inOrder = await Order.exists({ 'items.product': req.params.id });
    if (inOrder) {
      return res.status(400).json({ 
        error: "商品已有订单关联,无法删除,请先下架" 
      });
    }

    // 执行删除
    await Product.findByIdAndDelete(req.params.id);
    res.sendStatus(204);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

案例2:社交平台好友关系解除

javascript 复制代码
router.delete('/friendships/:friendId', userAuth, (req, res) => {
  const userId = req.user.id;
  const friendId = req.params.friendId;

  Friendship.findOneAndDelete({
    $or: [
      { user: userId, friend: friendId },
      { user: friendId, friend: userId }
    ]
  })
  .then(() => res.sendStatus(204))
  .catch(err => res.status(500).json(err));
});

DELETE 方法在需要移除资源的场景中非常有用,正确使用可以使 API 更加符合 RESTful 设计原则,同时保证操作的安全性和可预测性。对于重要数据,建议实现软删除机制而非直接物理删除。

5. PATCH - 数据编辑器(变更描述方向)

PATCH 是 HTTP 协议中用于部分更新资源的方法,在 RESTful API 设计中专门用于高效更新资源的特定字段。以下是 PATCH 方法的全面解析和最佳实践。

核心特性

PATCH 方法的三个关键特性:

  1. 部分更新 - 只修改资源的部分内容而非整体
  2. 非强制幂等 - 取决于具体实现(标准操作应是幂等的)
  3. 高效性 - 减少网络传输数据量
主要使用场景

1. 更新资源的部分字段

  • 场景:只需修改资源的少数属性时

  • 示例

    javascript 复制代码
    // 更新用户邮箱(不修改其他字段)
    router.patch('/users/:id', (req, res) => {
      const updates = {};
      if (req.body.email) updates.email = req.body.email;
      if (req.body.phone) updates.phone = req.body.phone;
      
      User.findByIdAndUpdate(
        req.params.id,
        { $set: updates },
        { new: true, runValidators: true }
      )
        .then(user => user ? res.json(user) : res.sendStatus(404))
        .catch(err => res.status(400).json(err));
    });

2. 增量修改数值字段

  • 场景:计数器、库存等数值的增减

  • 示例

    javascript 复制代码
    // 增加商品库存
    router.patch('/products/:id/inventory', (req, res) => {
      Product.findByIdAndUpdate(
        req.params.id,
        { $inc: { stock: req.body.quantity } },
        { new: true }
      )
        .then(product => res.json({ stock: product.stock }))
        .catch(err => res.status(400).json(err));
    });

3. 数组元素操作

  • 场景:添加/移除数组中的元素

  • 示例

    javascript 复制代码
    // 为用户添加兴趣标签
    router.patch('/users/:id/tags', (req, res) => {
      const { action, tag } = req.body;
      const update = action === 'add' 
        ? { $addToSet: { tags: tag } } 
        : { $pull: { tags: tag } };
      
      User.findByIdAndUpdate(req.params.id, update, { new: true })
        .then(user => res.json(user.tags))
        .catch(err => res.status(400).json(err));
    });

4. 状态转换

  • 场景:更新订单状态、任务进度等

  • 示例

    javascript 复制代码
    // 更新订单状态
    router.patch('/orders/:id/status', (req, res) => {
      OrderService.updateStatus(req.params.id, req.body.status)
        .then(order => res.json({ status: order.status }))
        .catch(err => res.status(400).json(err));
    });
与 PUT 方法的区别
特性 PATCH PUT
语义 部分更新 完整替换
请求体大小 通常较小 通常较大
幂等性 取决于实现 强制幂等
失败风险 可能产生部分更新 全有或全无
适用场景 大对象的小修改 小对象的完整更新

请求示例对比

javascript 复制代码
### PATCH 示例(只更新邮箱)
PATCH /users/123
Content-Type: application/json
{
  "email": "[email protected]"
}

### PUT 示例(必须提供全部字段)
PUT /users/123
Content-Type: application/json
{
  "name": "张三",
  "email": "[email protected]",
  "role": "member"
}
最佳实践
  1. 响应状态码

    • 成功:200 OK(返回完整资源)或 204 No Content
    • 无效操作:400 Bad Request
    • 资源不存在:404 Not Found
    • 冲突:409 Conflict
  2. 请求体设计

    • 使用标准格式如 JSON Patch:

      javascript 复制代码
      [
        { "op": "replace", "path": "/email", "value": "[email protected]" },
        { "op": "add", "path": "/tags", "value": "vip" }
      ]
    • 或简单字段更新:

      javascript 复制代码
      {
        "email": "[email protected]",
        "preferences.notifications": false
      }
  3. 并发控制

    • 实现乐观锁控制:
    javascript 复制代码
    router.patch('/documents/:id', (req, res) => {
      const { version, ...updates } = req.body;
      
      Document.findOneAndUpdate(
        { _id: req.params.id, version },
        { $set: updates, $inc: { version: 1 } },
        { new: true }
      )
        .then(doc => doc ? res.json(doc) : res.status(409).json({ error: "版本冲突" }))
        .catch(err => res.status(400).json(err));
    });
  4. 安全性

    • 验证可修改字段(防止越权修改):
    javascript 复制代码
    // 允许修改的字段白名单
    const ALLOWED_UPDATES = ['email', 'phone', 'preferences'];
    
    router.patch('/users/:id', (req, res) => {
      const updates = _.pick(req.body, ALLOWED_UPDATES);
      // 更新逻辑...
    });
实际案例

案例1:用户资料部分更新

javascript 复制代码
const ALLOWED_FIELDS = ['name', 'avatar', 'bio', 'location'];

router.patch('/profile', authMiddleware, async (req, res) => {
  try {
    // 过滤允许修改的字段
    const updates = _.pick(req.body, ALLOWED_FIELDS);
    if (_.isEmpty(updates)) {
      return res.status(400).json({ error: "无有效更新字段" });
    }

    // 执行更新
    const user = await User.findByIdAndUpdate(
      req.user.id,
      { $set: updates },
      { new: true, runValidators: true }
    ).select('-password');

    res.json(user);
  } catch (err) {
    if (err.name === 'ValidationError') {
      res.status(400).json({ error: err.message });
    } else {
      res.status(500).json({ error: "更新失败" });
    }
  }
});

案例2:工单系统状态流转

javascript 复制代码
const STATUS_FLOW = {
  'open': ['in_progress'],
  'in_progress': ['blocked', 'completed'],
  'blocked': ['in_progress']
};

router.patch('/tickets/:id/status', authMiddleware, async (req, res) => {
  try {
    const ticket = await Ticket.findById(req.params.id);
    if (!ticket) return res.sendStatus(404);

    // 验证状态转换是否合法
    const allowedTransitions = STATUS_FLOW[ticket.status] || [];
    if (!allowedTransitions.includes(req.body.status)) {
      return res.status(400).json({ 
        error: `无法从 ${ticket.status} 状态转换为 ${req.body.status}`
      });
    }

    // 执行状态更新
    ticket.status = req.body.status;
    ticket.history.push({
      event: 'status_change',
      from: ticket.status,
      to: req.body.status,
      changedBy: req.user.id
    });

    await ticket.save();
    res.json({ status: ticket.status });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

PATCH 方法是 RESTful API 中实现高效部分更新的最佳选择,特别适合大型资源的小规模修改场景。正确使用 PATCH 可以显著减少网络传输量,提高API性能,同时保持接口的语义清晰性。对于需要原子性操作的复杂更新,建议使用 JSON Patch 等标准格式。

6. HEAD - 元数据获取器(性能监控方向)

HEAD 方法是 HTTP 协议中一个特殊但非常有用的请求方法,它与 GET 方法类似但只获取响应头信息而不返回实际内容。以下是 HEAD 方法的全面应用场景解析。

核心特性

HEAD 方法的三个关键特性:

  1. 无响应体 - 只返回头部信息,不返回实际内容
  2. 安全性 - 不会修改服务器状态(与GET相同)
  3. 幂等性 - 多次执行相同请求结果一致
主要应用场景

1. 资源存在性检查

  • 场景:检查资源是否存在而不需要获取完整内容

  • 示例

    javascript 复制代码
    // 检查用户头像是否存在
    router.head('/avatars/:userId', (req, res) => {
      fs.stat(`avatars/${req.params.userId}.jpg`, (err) => {
        if (err) return res.sendStatus(404);
        res.set('Content-Type', 'image/jpeg').sendStatus(200);
      });
    });
  • 优势:比GET节省90%以上的带宽

2. 缓存验证

  • 场景:验证本地缓存是否仍然有效

  • 示例流程

    1. 客户端发送HEAD请求
    2. 服务器返回Last-Modified或ETag
    3. 客户端比较本地缓存决定是否使用缓存
  • 代码实现

    javascript 复制代码
    app.head('/articles/:id', (req, res) => {
      const article = getArticle(req.params.id);
      if (!article) return res.sendStatus(404);
      
      res.set({
        'ETag': article.versionHash,
        'Last-Modified': article.updatedAt.toUTCString()
      }).sendStatus(200);
    });

3. 获取元数据

  • 场景:获取资源元信息而不传输内容

  • 常见元数据

    • Content-Type
    • Content-Length
    • Last-Modified
    • CORS头信息
  • 实际案例

    javascript 复制代码
    HEAD /large-file.pdf HTTP/1.1
    
    HTTP/1.1 200 OK
    Content-Type: application/pdf
    Content-Length: 5242880  // 客户端可提前知道文件大小
    Accept-Ranges: bytes

4. 预检请求优化

  • 场景:替代OPTIONS方法进行更精确的CORS检查

  • 优势:可以获取到实际GET请求会返回的所有头信息

  • 实现示例

    javascript 复制代码
    app.head('/api/data', (req, res) => {
      res.set({
        'Access-Control-Allow-Origin': '*',
        'Content-Type': 'application/json',
        'X-RateLimit-Limit': '1000'
      }).sendStatus(200);
    });

5. 带宽敏感环境下的监控

  • 场景
    • 移动网络下的API健康检查
    • IoT设备资源监控
    • 大规模分布式系统的存活检测
  • 特点
    • 单次请求仅需100-200字节
    • 比GET请求节省90%以上的数据量
与GET方法的对比
特性 HEAD GET
响应体 永远为空 包含完整内容
带宽消耗 极小(仅头部) 大(头部+内容)
缓存验证 完全等效 完全等效
典型延迟 0.2-0.5x GET请求 基准值(1x)
浏览器支持 所有主流浏览器 所有主流浏览器
使用频率 较少(特殊场景) 极高(常规请求)
最佳实践
  1. 正确实现HEAD

    • 必须返回与GET相同的头信息
    • 状态码必须与GET一致
    javascript 复制代码
    // 正确实现示例
    app.get('/data', (req, res) => {
      res.set('Custom-Header', 'value').json({ data: '...' });
    });
    
    app.head('/data', (req, res) => {
      res.set('Custom-Header', 'value').sendStatus(200);
    });
  2. 性能优化技巧

    • 在Node.js中复用GET路由的处理逻辑:

      javascript 复制代码
      // Express示例
      app.get('/data', handleDataRequest);
      app.head('/data', handleDataRequest); // 相同处理器
      
      function handleDataRequest(req, res) {
        const data = getData();
        res.set('ETag', generateETag(data));
        
        // HEAD请求时提前返回
        if (req.method === 'HEAD') return res.sendStatus(200);
        
        res.json(data);
      }
  3. 缓存策略

    • 对HEAD请求返回Cache-Control

    • 配合ETag实现高效验证:

      javascript 复制代码
      HEAD /resource HTTP/1.1
      
      HTTP/1.1 200 OK
      ETag: "686897696a7c876b7e"
      Cache-Control: max-age=3600
  4. 错误处理

    • 必须与GET相同的错误响应:

      javascript 复制代码
      app.head('/users/:id', (req, res) => {
        const user = getUser(req.params.id);
        if (!user) {
          // 保持与GET一致的404响应
          return res.status(404).set('X-Error', 'Not found').end();
        }
        res.set('Content-Type', 'application/json').sendStatus(200);
      });
实际应用案例

案例1:大文件下载前的预检

javascript 复制代码
// 检查文件信息后再决定是否下载
router.head('/download/:fileId', (req, res) => {
  const file = db.getFile(req.params.fileId);
  if (!file) return res.sendStatus(404);
  
  res.set({
    'Content-Length': file.size,
    'Content-Type': file.mimeType,
    'Accept-Ranges': 'bytes',
    'X-File-Hash': file.md5Hash
  }).sendStatus(200);
});

// 客户端使用示例
async function checkBeforeDownload(fileId) {
  const res = await fetch(`/download/${fileId}`, { method: 'HEAD' });
  if (!res.ok) throw new Error('File not exists');
  
  const size = res.headers.get('Content-Length');
  if (size > 100000000) { // 100MB
    if (!confirm(`下载 ${size} 字节的大文件?`)) return;
  }
  startDownload(fileId);
}

案例2:分布式系统健康检查

javascript 复制代码
// 轻量级健康检查端点
router.head('/health', (req, res) => {
  const health = {
    db: checkDatabase(),
    cache: checkRedis(),
    disk: checkDiskSpace()
  };
  
  const isHealthy = Object.values(health).every(Boolean);
  res.set('X-Health-Status', JSON.stringify(health))
     .status(isHealthy ? 200 : 503)
     .end();
});

// 监控系统每分钟调用HEAD /health
// 仅消耗极少的网络资源

HEAD 方法是 HTTP 协议设计精妙的体现,在特定场景下能显著提升系统效率。合理使用 HEAD 可以构建出更加高效、专业的 Web 服务和 API。

7. OPTIONS - 能力探查器(API 自描述方向)

OPTIONS 方法是 HTTP 协议中用于获取资源通信选项的重要方法,在现代 Web 开发中主要应用于 CORS(跨域资源共享)预检请求。以下是 OPTIONS 方法的全面解析和实际应用场景。

核心特性

OPTIONS 方法的三个关键特性:

  1. 元数据获取 - 获取目标资源支持的通信选项
  2. 安全性 - 不会修改服务器状态
  3. 幂等性 - 多次执行相同请求结果一致
主要应用场景

1. CORS 预检请求(最主要场景)

  • 场景:跨域请求前的预检(Preflight)

  • 流程

    1. 浏览器自动发送 OPTIONS 请求
    2. 服务器返回允许的方法和头信息
    3. 浏览器决定是否发送实际请求
  • 示例

    javascript 复制代码
    // CORS 中间件实现
    app.options('/api/data', (req, res) => {
      res.set({
        'Access-Control-Allow-Origin': 'https://example.com',
        'Access-Control-Allow-Methods': 'GET,POST,PUT',
        'Access-Control-Allow-Headers': 'Content-Type,Authorization',
        'Access-Control-Max-Age': '86400' // 缓存1天
      }).sendStatus(204);
    });

2. API 能力发现

  • 场景:动态获取 API 端点支持的方法

  • 示例:

    javascript 复制代码
    OPTIONS /users/123 HTTP/1.1
    
    HTTP/1.1 200 OK
    Allow: GET, PUT, DELETE, PATCH
    Link: </users/123>; rel="canonical"

3. 服务器功能探测

  • 场景:检查服务器支持的 HTTP 方法

  • 实现:

    javascript 复制代码
    app.options('*', (req, res) => {
      res.set('Allow', 'GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH')
         .sendStatus(204);
    });

4. WebDAV 协议支持

  • 场景:WebDAV 服务器必须实现 OPTIONS 方法

  • 响应示例

    复制代码
    OPTIONS /webdav/ HTTP/1.1
    
    HTTP/1.1 200 OK
    DAV: 1, 2, 3
    Allow: OPTIONS, GET, HEAD, POST, PUT, DELETE, PROPFIND, MKCOL
CORS 预检深度解析

触发条件(浏览器自动发起)

  1. 跨域请求
  2. 非简单请求(满足任一条件):
    • 使用非简单方法(PUT/DELETE等)
    • 包含自定义头(如 Authorization)
    • Content-Type 非简单值(如 application/json)

典型预检流程

复制代码
sequenceDiagram
    Browser->>Server: OPTIONS /api (预检请求)
    Server->>Browser: 204 No Content (返回CORS头)
    Browser->>Server: POST /api (实际请求)
    Server->>Browser: 200 OK (实际响应)

关键响应头

响应头 作用 示例值
Access-Control-Allow-Origin 允许的源 https://example.com
Access-Control-Allow-Methods 允许的方法 GET,POST,PUT
Access-Control-Allow-Headers 允许的请求头 Content-Type,Authorization
Access-Control-Max-Age 预检结果缓存时间(秒) 86400 (24小时)
Access-Control-Allow-Credentials 是否允许发送凭据 true
最佳实践

1. 全局 OPTIONS 处理

javascript 复制代码
// Express 全局中间件
app.use((req, res, next) => {
  if (req.method === 'OPTIONS') {
    res.set({
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE,OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type,Authorization',
      'Access-Control-Max-Age': '86400'
    }).sendStatus(204);
  } else {
    next();
  }
});

2. 生产环境配置建议

javascript 复制代码
const CORS_CONFIG = {
  origin: process.env.ALLOWED_ORIGINS.split(','), // 'https://example.com,https://admin.example.com'
  methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
  maxAge: 86400,
  credentials: true
};

app.options('*', cors(CORS_CONFIG)); // 使用cors中间件

3. 性能优化

  • 缓存控制 :设置合理的 Access-Control-Max-Age
  • 精简响应:204 No Content 比 200 OK 更合适
  • 按需配置:不同路径可设置不同的 CORS 策略

4. 安全注意事项

javascript 复制代码
// 严格限制来源
app.options('/api', (req, res) => {
  const allowedOrigins = ['https://example.com', 'https://admin.example.com'];
  const origin = req.headers.origin;
  
  if (allowedOrigins.includes(origin)) {
    res.set({
      'Access-Control-Allow-Origin': origin,
      'Vary': 'Origin' // 避免缓存污染
    });
  }
  // ...其他头设置
});
实际应用案例

案例1:REST API 跨域支持

javascript 复制代码
// API服务器配置
app.options('/api/*', (req, res) => {
  res.set({
    'Access-Control-Allow-Origin': 'https://myapp.com',
    'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
    'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-API-Version',
    'Access-Control-Max-Age': '3600',
    'Vary': 'Origin, Access-Control-Request-Headers'
  }).sendStatus(204);
});

// 前端调用示例
fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token123'
  },
  body: JSON.stringify({ data: 'test' })
});

案例2:微服务能力发现

javascript 复制代码
// 服务发现端点
app.options('/service-info', (req, res) => {
  res.set({
    'Allow': 'GET, OPTIONS',
    'Link': '</docs>; rel="documentation"',
    'X-API-Version': '1.2.0',
    'X-Supported-Features': 'batch,search,pagination'
  }).json({
    service: 'user-service',
    endpoints: ['/users', '/users/:id', '/profile']
  });
});

OPTIONS 方法是现代 Web 开发中实现安全跨域通信的基础,正确配置 OPTIONS 响应是构建开放 API 服务的关键环节。通过合理设置 CORS 头,可以兼顾安全性和跨域访问需求。

三、🔄 请求-响应生命周期(工业级实现)

复制代码
[客户端请求]
  ├─ 1. 方法验证 (405 Method Not Allowed)
  ├─ 2. 头部检查 (400 Bad Request)
  ├─ 3. 身份认证 (401 Unauthorized)
  ├─ 4. 权限验证 (403 Forbidden)
  ├─ 5. 速率限制 (429 Too Many Requests)
  ├─ 6. 请求解析 (413 Payload Too Large)
  ├─ 7. 业务处理
  │    ├─ 数据验证 (422 Unprocessable Entity)
  │    ├─ 事务管理
  │    └─ 并发控制 (409 Conflict)
  └─ 8. 响应构造
       ├─ 数据转换 (Content Negotiation)
       ├─ 缓存头设置
       ├─ 安全头设置
       └─ 链路追踪 (X-Request-ID)

四、🌈 方法选择决策树(业务场景导向)

复制代码
需要改变服务器状态吗?
├─ 否 → GET/HEAD
└─ 是 → 操作性质?
     ├─ 创建资源 → POST (客户端不指定ID) / PUT (客户端指定ID)
     ├─ 更新资源 → 
     │    ├─ 完全替换 → PUT
     │    └─ 部分更新 → PATCH
     ├─ 删除资源 → DELETE
     └─ 特殊操作 → 
          ├─ 检查存在性 → HEAD
          ├─ 跨域预检 → OPTIONS
          ├─ 幂等操作 → PUT/DELETE
          └─ 非幂等操作 → POST

五、💎 专业实践建议(生产环境级)

1. 高级幂等性控制

javascript 复制代码
// 使用 Redis 实现幂等令牌
router.post('/orders', async (req, res) => {
  const idempotencyKey = req.headers['x-idempotency-key'];
  if (idempotencyKey) {
    const cached = await redis.get(`idempotency:${idempotencyKey}`);
    if (cached) {
      return res.json(JSON.parse(cached));
    }
  }
  
  const order = createOrder(req.body);
  
  if (idempotencyKey) {
    await redis.setex(
      `idempotency:${idempotencyKey}`,
      24 * 3600,
      JSON.stringify(order)
    );
  }
  
  res.status(201).json(order);
});

2. 批量操作设计模式

javascript 复制代码
// 批量操作混合方法
router.route('/resources/batch')
  .get((req, res) => {
    // 批量查询 ?ids=1,2,3
    const items = getBatchItems(req.query.ids.split(','));
    res.json({ items });
  })
  .post((req, res) => {
    // 批量创建
    const results = req.body.items.map(createItem);
    res.status(207).json({ results }); // 207 Multi-Status
  })
  .delete((req, res) => {
    // 批量删除
    const results = req.body.ids.map(id => ({
      id,
      status: deleteItem(id) ? 204 : 404
    }));
    res.status(207).json({ results });
  });

3. 内容协商进阶实现

javascript 复制代码
// 支持多种响应格式
router.get('/smart-data', (req, res) => {
  const data = getSmartData();
  
  res.format({
    'application/json': () => res.json(data),
    'application/xml': () => {
      res.type('xml');
      res.send(convertToXML(data));
    },
    'text/csv': () => {
      res.attachment('data.csv');
      res.csv(data);
    },
    default: () => res.status(406).json({
      error: 'Not Acceptable',
      supported: ['json', 'xml', 'csv']
    })
  });
});

4. 超媒体控制 (HATEOAS)

javascript 复制代码
// 动态生成资源链接
router.get('/orders/:id', (req, res) => {
  const order = getOrder(req.params.id);
  
  const links = {
    self: { href: `/orders/${order.id}`, method: 'GET' },
    update: { href: `/orders/${order.id}`, method: 'PATCH' },
    cancel: { 
      href: `/orders/${order.id}/cancel`,
      method: 'POST',
      condition: order.status === 'pending'
    },
    payment: {
      href: `/payments?orderId=${order.id}`,
      method: 'GET'
    }
  };
  
  res.json({
    ...order,
    _links: Object.fromEntries(
      Object.entries(links)
        .filter(([_, value]) => value.condition !== false)
    )
  });
});

六、🚀 性能优化专项

1. 条件请求深度优化

javascript 复制代码
// 基于内容哈希的ETag验证
router.get('/high-performance', (req, res) => {
  const data = getFrequentlyChangingData();
  const hash = createHash('sha256').update(JSON.stringify(data)).digest('hex');
  
  if (req.headers['if-none-match'] === hash) {
    return res.status(304).end();
  }
  
  res.set('ETag', hash)
     .set('Cache-Control', 'public, max-age=10')
     .json(data);
});

2. 流式响应处理

javascript 复制代码
// 处理大文件下载
router.get('/large-file', (req, res) => {
  const fileStream = fs.createReadStream('./large-file.bin');
  
  res.set({
    'Content-Type': 'application/octet-stream',
    'Content-Disposition': 'attachment; filename="large-file.bin"',
    'Transfer-Encoding': 'chunked'
  });
  
  fileStream.on('error', (err) => {
    console.error('Stream error:', err);
    res.status(500).end();
  });
  
  fileStream.pipe(res);
});

七、🔒 安全加固方案

1. 方法过滤中间件

javascript 复制代码
// 限制危险方法
app.use((req, res, next) => {
  const dangerousMethods = ['TRACE', 'CONNECT'];
  if (dangerousMethods.includes(req.method)) {
    return res.status(405).json({
      error: 'Method Not Allowed',
      allowed: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']
    });
  }
  next();
});

2. CORS 精细控制

javascript 复制代码
// 动态CORS配置
router.options('/api/*', (req, res) => {
  const origin = req.headers.origin;
  const allowedOrigins = getConfiguredOrigins();
  
  if (allowedOrigins.includes(origin)) {
    res.set({
      'Access-Control-Allow-Origin': origin,
      'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
      'Access-Control-Max-Age': '86400',
      'Vary': 'Origin'
    });
  }
  
  res.status(204).end();
});

通过以上全面的方法解析和工程实践,开发者可以构建出既符合 RESTful 规范,又能满足复杂业务需求的高质量 API 系统。记住,优秀的 API 设计不仅是技术实现,更是与客户端开发者的一种契约和沟通方式。

相关推荐
等一个晴天丶2 小时前
node爬虫实战:爬取世纪佳缘交友信息
node.js
有仙则茗4 小时前
process.cwd()和__dirname有什么区别
前端·javascript·node.js
水冗水孚8 小时前
面试官:你是前端你了解oss吗?我反手写了一个react+express+minio实现oss文件存储功能
react.js·node.js·express
华洛9 小时前
《从0到1打造企业级AI售前机器人——实战指南五:处理用户意图的细节实现!》
javascript·vue.js·node.js
树獭叔叔1 天前
从零开始Node之旅——装饰器
后端·node.js
程序猿小D1 天前
第27节 Node.js Buffer
linux·开发语言·vscode·node.js·c#·编辑器·vim
大磕学家ZYX1 天前
使用Nodejs尝试小程序后端服务编写:简单的待办事项管理demo
开发语言·javascript·小程序·node.js
GISer_Jing2 天前
前端构建工具(Webpack\Vite\esbuild\Rspack)拆包能力深度解析
前端·webpack·node.js