Strapi+react-admin动态设置Content-Range头及技术问询
Content-Range Headers for All Strapi Resources (react-admin Integration) Hey there! I get where you're coming from—hardcoding the Content-Range header in every controller is a total pain, especially with 40+ models (and more coming!). Let's fix this with scalable, maintainable solutions that work across all your Strapi resources for react-admin.
First, a quick recap: react-admin requires the Content-Range header (in the format resource start-end/total) on list endpoints to handle pagination. Instead of repeating code per controller, we can automate this globally.
Option 1: Global Middleware (Recommended for Flexibility)
Strapi lets you create custom middleware that runs on every request. We'll build one that targets all find (fetchAll) requests, calculates the total count for the target model, and sets the header automatically.
Step 1: Create the Middleware File
Create ./middlewares/add-content-range.js with this code:
module.exports = strapi => { return { initialize() { strapi.app.use(async (ctx, next) => { // Let the request process first await next(); // Only handle GET requests for the "find" handler (react-admin's fetchAll) if (ctx.method === 'GET' && ctx.state.route?.handler === 'find') { try { const model = ctx.state.route.model; if (!model) return; // Get total count of records for the model const total = await strapi.query(model).count(); // Parse pagination params from react-admin's request (with defaults) const limit = parseInt(ctx.query._limit) || total; const start = parseInt(ctx.query._start) || 0; const end = Math.min(start + limit - 1, total - 1); // Set required headers ctx.set('Content-Range', `${model} ${start}-${end}/${total}`); // Expose the header to react-admin (critical for CORS) ctx.set('Access-Control-Expose-Headers', 'Content-Range'); } catch (err) { strapi.log.error('Failed to set Content-Range header:', err); } } }); }, }; };
Step 2: Register the Middleware
Update ./config/middleware.js to load your new middleware early in the stack (before response-handling middlewares):
module.exports = { settings: { // Keep your existing settings here }, load: { before: ['add-content-range', 'responseTime', 'logger', 'cors', 'responses', 'gzip'], order: [ // Keep your existing order here ], after: [ // Keep your existing after middlewares here ], }, };
Option 2: Extend Strapi's Core Controller
If you prefer modifying the core controller logic instead of using middleware, you can override the base find method that all resource controllers inherit from. This ensures every model's list endpoint gets the header.
Step 1: Extend the Content Manager Controller
Create ./extensions/content-manager/controllers/ContentManager.js with this code:
const { Controller } = require('strapi-plugin-content-manager').controllers; module.exports = { // Spread the original controller methods ...Controller, // Override the find method async find(ctx) { // Run the original find logic to get the data const result = await super.find(ctx); const model = ctx.state.route.model; if (model) { try { const total = await strapi.query(model).count(); const limit = parseInt(ctx.query._limit) || total; const start = parseInt(ctx.query._start) || 0; const end = Math.min(start + limit - 1, total - 1); ctx.set('Content-Range', `${model} ${start}-${end}/${total}`); ctx.set('Access-Control-Expose-Headers', 'Content-Range'); } catch (err) { strapi.log.error('Failed to set Content-Range in extended controller:', err); } } // Return the original result return result; }, };
Key Notes for Both Options
- CORS Exposure: The
Access-Control-Expose-Headersheader is mandatory—react-admin can't readContent-Rangeotherwise, thanks to browser CORS rules. - Pagination Fallbacks: We set defaults for
_limitand_startso the header works even if the request doesn't include them. - Error Handling: We wrap the count logic in a try/catch to avoid breaking the entire request if something goes wrong with counting.
- Users-Permissions Compatibility: Both solutions work for the built-in User model from the
users-permissionsplugin, since it uses the same controller/route structure.
Why This Works for Your Stack
Your setup (Strapi v3.0.0-alpha.25.2, Node 11.x) supports both middleware and controller extensions perfectly. No need to modify individual model controllers anymore—new resources you add will automatically get the Content-Range header without extra work.
内容的提问来源于stack exchange,提问作者TheRealBarenziah

