Strapi 4自定义中间件无法阻止文章取消发布:如何获取请求体?
解决Strapi 4自定义中间件无法获取请求体及阻止文章取消发布的问题
问题根源分析
- 中间件执行顺序错误:自定义中间件如果在
bodyParser之前运行,请求体还未被解析,自然拿不到context.request.body。 - 请求方法与端点判断错误:取消发布文章的操作不是POST请求(POST用于创建内容),而是通过PUT/PATCH更新
publishedAt字段,或者POST调用专门的unpublish动作端点。 - 请求体结构错误: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
相关产品推荐
相关产品推荐

