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

Strapi 4自定义中间件无法阻止文章取消发布:如何获取请求体?

解决Strapi 4自定义中间件无法获取请求体及阻止文章取消发布的问题

问题根源分析

  1. 中间件执行顺序错误:自定义中间件如果在bodyParser之前运行,请求体还未被解析,自然拿不到context.request.body。
  2. 请求方法与端点判断错误:取消发布文章的操作不是POST请求(POST用于创建内容),而是通过PUT/PATCH更新publishedAt字段,或者POST调用专门的unpublish动作端点。
  3. 请求体结构错误:Strapi Content Manager的请求体数据嵌套在data字段下,直接访问body.publishedAt无法获取到值。

解决方案一:修正自定义中间件

1. 调整中间件执行顺序

在config/middlewares.ts中,将自定义的prevent-unpublish放在strapi::body(即body解析中间件)之后,确保请求体已被解析:

export default [
  'strapi::errors',
  'strapi::security',
  'strapi::cors',
  'strapi::poweredBy',
  'strapi::logger',
  'strapi::query',
  'strapi::body', // 必须在自定义中间件之前
  'strapi::session',
  'strapi::favicon',
  'strapi::public',
  './middlewares/prevent-unpublish', // 自定义中间件位置
];

2. 更新中间件代码

修正请求方法、端点判断及请求体获取逻辑,覆盖两种取消发布的场景:

// src/middlewares/prevent-unpublish.ts
module.exports = (config, { strapi }) => {
  return async (context, next) => {
    const { method, url, body } = context.request;
    // 匹配文章更新端点(如/content-manager/collection-types/api::post.post/123)
    const isPostUpdateEndpoint = url.match(/\/content-manager\/collection-types\/api::post\.post\/\d+/);
    // 匹配专门的取消发布动作端点
    const isUnpublishAction = url.match(/\/content-manager\/collection-types\/api::post\.post\/\d+\/actions\/unpublish/);

    // 判断是否需要拦截:更新文章的PUT/PATCH,或调用unpublish动作的POST
    const shouldCheck = (['PUT', 'PATCH'].includes(method) && isPostUpdateEndpoint) || 
                       (method === 'POST' && isUnpublishAction);

    if (shouldCheck) {
      strapi.log.info('Detected unpublish attempt:', method, url);
      
      // 拦截更新publishedAt为null的请求
      if (['PUT', 'PATCH'].includes(method)) {
        if (body?.data?.publishedAt === null) {
          return context.throw(403, 'Unpublishing posts is not allowed.');
        }
      }
      // 拦截直接调用unpublish动作的请求
      if (method === 'POST' && isUnpublishAction) {
        return context.throw(403, 'Unpublishing posts is not allowed.');
      }
    }

    await next();
  };
};

解决方案二:使用Strapi生命周期钩子(更推荐)

相比中间件,Strapi提供了专门的生命周期钩子来处理内容发布状态变更,逻辑更简洁直接:
在api/post/content-types/post/lifecycles.ts中添加以下代码:

export default {
  // 拦截更新时设置publishedAt为null的操作
  async beforeUpdate(event) {
    const { data } = event.params;
    if (data?.publishedAt === null) {
      throw new Error('Unpublishing posts is not allowed.');
    }
  },
  // 直接拦截取消发布动作
  async beforeUnpublish() {
    throw new Error('Unpublishing posts is not allowed.');
  },
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 04:32:43