bibrokhim/laravel-force-json-response
Laravel middleware that forces every request to accept JSON by setting the Accept header to application/json when missing or different, ensuring your app consistently returns JSON responses. Install via composer require bibrokhim/laravel-force-json-response.
Installation:
composer require bibrokhim/laravel-force-json-response
Publish the config file (if needed):
php artisan vendor:publish --provider="Bibrokhim\ForceJsonResponse\ForceJsonResponseServiceProvider"
Enable the Package:
Add the middleware to your HTTP kernel (app/Http/Kernel.php):
protected $middleware = [
// ...
\Bibrokhim\ForceJsonResponse\Middleware\ForceJsonResponse::class,
];
First Use Case:
Immediately, all HTTP responses (except those explicitly returning JSON) will be converted to JSON format. Test by making a request to any route—non-JSON responses (e.g., HTML, plain text) will now return as JSON with a data key:
{
"data": "Your response content"
}
Response::json() calls for non-JSON responses.Exclude Routes:
Override the config (config/force-json-response.php) to exclude specific routes or middleware groups:
'except' => [
'web', // Exclude web middleware group
'admin/*', // Exclude admin routes
],
Conditional JSON: Temporarily disable JSON forcing for specific routes by wrapping logic in middleware:
public function handle($request, Closure $next) {
$response = $next($request);
return $this->shouldForceJson($request) ? $response : $response->setContent(json_encode($response->getContent()));
}
Custom JSON Structure: Extend the middleware to modify the JSON wrapper:
// app/Http/Middleware/ForceJsonResponse.php
public function handle($request, Closure $next) {
$response = $next($request);
return response()->json(['custom_key' => $response->getContent()]);
}
Testing: Mock the middleware in tests to verify JSON responses:
$response = $this->withMiddleware(\Bibrokhim\ForceJsonResponse\Middleware\ForceJsonResponse::class)
->get('/api/endpoint');
$response->assertJsonStructure(['data']);
Unexpected HTML Responses: If your app serves HTML (e.g., Blade templates), ensure routes are excluded to avoid breaking frontend rendering. Example:
'except' => ['web', 'sanctum/csrf-cookie'], // Exclude web and CSRF routes
File Downloads: Binary responses (e.g., PDFs, images) may be incorrectly wrapped in JSON. Exclude these routes or handle them separately:
if ($request->is('downloads/*')) {
return $next($request);
}
Caching Headers:
JSON-wrapped responses may interfere with caching logic (e.g., Cache-Control). Verify headers in responses:
$response->header('Cache-Control', 'public, max-age=3600');
Check Middleware Order:
Ensure ForceJsonResponse runs after TrimStrings, ConvertEmptyStringsToNull, and before ConvertEmptyStringsToNull if you rely on these for data normalization.
Log Excluded Routes: Temporarily log excluded routes to debug misconfigurations:
\Log::debug('Excluded route:', [$request->path()]);
Custom JSON Wrapper:
Override the wrapJson() method in the middleware to customize the response structure:
protected function wrapJson($content) {
return response()->json(['meta' => ['status' => 'success'], 'data' => $content]);
}
Dynamic Exclusions: Use route middleware to conditionally exclude JSON forcing:
// Route
Route::get('/dynamic', function () {
// ...
})->middleware('throttle:10,1')->middleware('json:exclude');
Performance: For high-traffic APIs, benchmark the overhead of JSON wrapping. Consider caching responses to mitigate impact.
Fallback for Non-JSON Clients:
Add logic to detect client expectations (e.g., Accept: text/html) and bypass JSON forcing:
if ($request->wantsJson() === false) {
return $next($request);
}
How can I help you explore Laravel packages today?