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": "zhang@example.com"
    }
    
    // 避免嵌套过深
    {
      "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": "zhang@example.com", "age": 30 }

// PATCH 示例 - 只更新邮箱
PATCH /users/123
Body: { "email": "new@example.com" }
最佳实践
  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": "new@example.com"
}

### PUT 示例(必须提供全部字段)
PUT /users/123
Content-Type: application/json
{
  "name": "张三",
  "email": "new@example.com",
  "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": "new@example.com" },
        { "op": "add", "path": "/tags", "value": "vip" }
      ]
    • 或简单字段更新:

      javascript 复制代码
      {
        "email": "new@example.com",
        "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 设计不仅是技术实现,更是与客户端开发者的一种契约和沟通方式。

相关推荐
yzzzzzzzzzzzzzzzzz3 小时前
node.js之Koa框架
node.js
Java陈序员4 小时前
轻松设计 Logo!一款 Pornhub 风格的 Logo 在线生成器!
vue.js·node.js·vite
gongzemin6 小时前
使用Node.js开发微信第三方平台后台
微信小程序·node.js·express
JavaDog程序狗12 小时前
【软件环境】Windows安装NVM
前端·node.js
自学也学好编程12 小时前
【工具】NVM完全指南:Node.js版本管理工具的安装与使用详解
node.js
Moment13 小时前
调试代码,是每个前端逃不过的必修课 😏😏😏
前端·javascript·node.js
萌萌哒草头将军1 天前
Prisma ORM 又双叒叕发布新版本了!🚀🚀🚀
前端·javascript·node.js
zyfts1 天前
手把手教学Nestjs对excel的增删改查
前端·node.js
星空下的曙光2 天前
nodejs项目中常用的npm包及分类
node.js
墨菲安全2 天前
NPM组件 @0xme5war/apicli 等窃取主机敏感信息
前端·npm·node.js·主机信息窃取·npm恶意包·npm投毒