How to enforce JSON format in API Requests and Responses
Best Practices for Structuring, Validating, and Enforcing JSON in Laravel API Development
Introduction
There are several ways to ensure standard JSON format in API requests and responses which enforces consistent and structured data exchange. In this article, we’ll explore how to enforce JSON format on incoming requests in a Laravel application and outgoing responses.
How to enforce Request in JSON Format
When designing APIs in Laravel, it is important to enforce requests to be in JSON format. This helps ensure consistency, prevents unexpected request formats (such as HTML or other types other than JSON), and avoids receiving data from random or unintended sources. Additionally, enforcing JSON format improves security and simplifies request handling.
Here’s out to enforce API request to be in JSON format in Laravel.
Create a middleware EnforceJsonRequest
php artisan make:middleware EnforceJsonRequest
The EnforceJsonRequest middleware is located in app/Http/Middleware/EnforceJsonRequest.php
Define EnforceJsonRequest
Middleware Condition
- Open the
EnforceJsonRequest.php
and define the middleware conditions to intercept requests and check the format
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnforceJsonRequest
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
$contentType = $request->header('Content-Type');
if (strpos($contentType, 'application/json') === false && strpos($contentType, 'multipart/form-data') === false) {
return response()->json(['error' => 'Invalid Content-Type. Must be JSON or multipart/form-data.'], Response::HTTP_EXPECTATION_FAILED);
}
return $next($request);
}
}
The code validates the request's Content-Type
header to ensure it is either application/json
or multipart/form-data
(commonly used for file uploads in APIs). If the request does not meet this requirement, it returns an error response, preventing invalid or unexpected data formats.
Register the EnforceJsonRequest middleware
- Register the middleware in the
withMiddleware
callback function inbootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->trustProxies(at: '*');
$middleware->alias([
'enforce-json-request' => EnforceJsonRequest::class,
]);
})
Set EnforceJsonRequest Middleware to Routes
Apply the middleware to the API routes that must receive request in json format:
In a fresh Laravel application, the api.php
setup is not included by default. However, you can generate it using the command php artisan install:api
or manually create the api.php
file and register it within the withRouting
method in bootstrap/app.php
.
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
- Create the
api.php
file and set the middleware on the routes
<?php
use Illuminate\Support\Facades\Route;
Route::prefix('/v1')->middleware(['enforce-json-request'])->group(function () {
Route::get('/', function () {
return response()->json([
'message' => 'Request came in JSON Format',
], 200);
});
});
Serve the application using the php artisan serve
command:
Incoming Request in Action
- Request to the application without
content-type
header ofapplication/json
- Request with
content-type
set toapplication/json
How to enforce JSON response
Laravel checks the Accept
header in the request to determine the appropriate response. However, browsers and Postman often send Accept: */*
, so simply rewriting this header ensures Laravel handles the rest.
Create the EnforceJsonResponse
Middleware
Use the command below to create a middleware with any name of your choice.
php artisan make:middleware EnforceJsonResponse
Define the EnforceJsonResponse Middleware condition
- Update the
EnforceJsonResponse
middleware in theapp/Http/Middleware/EnforceJsonResponse.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnforceJsonResponse
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
$request->headers->set('Accept', 'application/json');
$response = $next($request);
if (!$response instanceof JsonResponse) {
return response()->json($response->original ?? ['message' => 'Invalid response'], $response->status());
}
return $response;
}
}
Register the middleware EnforceJsonResponse
Register the middleware in bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->trustProxies(at: '*');
$middleware->alias([
'enforce-json-request' => EnforceJsonRequest::class,
'enforce-json-response' => EnforceJsonResponse::class,
]);
})
Enforce Response Middleware on Routes
- Define a route that returns a response not in json format
<?php
use Illuminate\Support\Facades\Route;
Route::prefix('/v1')->middleware(['enforce-json-request'])->group(function () {
Route::get('/', function () {
return 'Request came in JSON Format';
});
});
- Output is in HTML Format when request is made without
enforce-json-response
on the route returning response.
- Add the
enforce-json-response
to the API route
<?php
use Illuminate\Support\Facades\Route;
Route::prefix('/v1')->middleware(['enforce-json-request', 'enforce-json-response'])->group(function () {
Route::get('/', function () {
return 'Request came in JSON Format';
});
});
- Or use
appendToGroup
on withMiddleware callback function in thebootstrap/app.php
to keep the api route clean
->withMiddleware(function (Middleware $middleware) {
$middleware->trustProxies(at: '*');
$middleware->alias([
// 'enforce-json-request' => EnforceJsonRequest::class,
// 'enforce-json-response' => EnforceJsonResponse::class,
]);
$middleware->appendToGroup('enforce-json', [
EnforceJsonResponse::class,
EnforceJsonRequest::class
]);
})
- API route
<?php
use Illuminate\Support\Facades\Route;
Route::prefix('/v1')->middleware(['enforce-json'])->group(function () {
Route::get('/', function () {
return 'Request came in JSON Format';
});
});
Response in Action
- Output in JSON format when request is made with
enforce-json-response
on the route returns response.
Handling Exceptions response in JSON format
->withExceptions(function (Exceptions $exceptions) {
$exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $th) {
return $request->is('api/*') || $request->expectsJson();
});
})->create();
- Returns error in JSON format but with too much technical details needed for debugging purposes only
{
"message": "syntax error, unexpected token \"}\", expecting \";\"",
"exception": "ParseError",
"file": "/path to error file",
"line": 13,
"trace": [
]
}
- You can define a global exception handling error response in JSON to return the message alone
->withExceptions(function (Exceptions $exceptions) {
$exceptions->renderable(function (Throwable $e, Request $request) {
return new JsonResponse([
'message' => $e->getMessage(),
], $e->getCode() ?: 500);
});
})
Handle individual exception response in JSON format
Alternatively, you can handle individual exceptions by returning custom responses in JSON format.
->withExceptions(function (Exceptions $exceptions) {
$exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $th) {
return $request->is('api/*') || $request->expectsJson();
});
$exceptions->renderable(function (ParseError $e) {
return response()->json([
'message' => "Syntax error",
], Response::HTTP_INTERNAL_SERVER_ERROR);
});
})
- Output
NotFoundHttpException
, ParseError
, MethodNotAllowedHttpException
etc. You can customize how these errors are handled to make the error message clear and user friendly while logging technical details for debugging purpose.Conclusion
You can define and structure request and responses in Laravel when building APIs in JSON format. It makes provision for consistency, clean code, proper data exchange format etc. In this article, we’ve explore best practices for enforcing JSON format in Laravel, including request validation, response formatting, and error handling to create well-structured and predictable APIs. Implementing these best practices enhances API performance and improves integration with frontend applications and third-party services. Laravel developers can build more robust and maintainable APIs, by prioritizing JSON enforcement.
Find this article useful… kindly share with your network and feel free to use the comment section for questions, answers, and contributions.