Let’s say your APIs only provide responses in JSON format.
You want your APIs to serve only those clients who include the Accept: application/json
header in their requests as this means they explicitly understand and want to receive responses in JSON format.
If a client doesn’t include this header, you want to send them an error message.
Laravel doesn’t handle this by default, so we’ll need to add this check ourselves.
🎥 Consider watching this video as well to supplement your learning:
Create a Middleware
A good way to implement the check is by using a Middleware.
A Middleware lets us apply the check to all APIs or just specific ones.
To create the Middleware, run the following command:
php artisan make:middleware ForceJsonResponse
In ForceJsonResponse
Middleware we check if the client wants JSON.
If it doesn’t, we will return a Content-Type: text/plain
response with a descriptive error message informing the client that they need to use the Accept: application/json
header.
<?php
declare(strict_types = 1);
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class ForceJsonResponse
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if (!$request->wantsJson()) {
return response(
'Unsupported request format. Set HTTP header to Accept: application/json',
Response::HTTP_NOT_ACCEPTABLE
);
}
return $next($request);
}
}
We can now use the ForceJsonResponse
Middleware in multipe ways.
Use Middleware on an API namespace
If we want to apply the Middleware to all the APIs under the api
namespace, then we have to register it in the bootstrap/app.php
file:
// bootstrap/app.php
<?php
use Illuminate\Foundation\Application;
use App\Http\Middleware\ForceJsonResponse;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [
\App\Http\Middleware\HandleInertiaRequests::class,
\Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets::class,
]);
// This 👇
$middleware->api(append: [
ForceJsonResponse::class
]);
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();
If the APIs are protected by an authentication middleware, then you would want the ForceJsonResponse
Middleware to be called first.
To do that, you should use prepend
instead of append
:
$middleware->api(prepend: [
ForceJsonResponse::class
]);
Use Middleware in a Route group
If we want to use it as a route middleware group, then it would look like this:
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Middleware\ForceJsonResponse;
use Symfony\Component\HttpFoundation\Response;
Route::middleware([ForceJsonResponse::class])->group(function () {
Route::post('/products', function (Request $request) {
return response()->json([
'message' => "We're requesting POST /api/products",
], Response::HTTP_OK);
});
Route::post('/customers', function (Request $request) {
return response()->json([
'message' => "We're requesting POST /api/customers",
], Response::HTTP_OK);
});
});
Use Middleware only for one API
Or if we want to use the middleware only for one API, it would look like this:
<?php
use App\Http\Middleware\ForceJsonResponse;
use Symfony\Component\HttpFoundation\Response;
Route::post('/products', function (Request $request) {
return response()->json([
'message' => "We're requesting POST /api/products",
], Response::HTTP_OK);
})->middleware(ForceJsonResponse::class);
Result
Now all that’s left is to test the APIs and make sure they work properly.
In the image below, I use Postman to call an API to which I have applied the middleware.
Because I did not set the Accept: application/json
header, the middleware intervenes and responds with an error message, notifying me that I need to specify the header in the request.
Thus, everything works as expected. ✅
In this example, I added the Accept: application/json
header, and the API responded with the correct message. ✅
That was it. Happy coding 🥳
Let me know what you think about this article in the comments section below.
If you find this article helpful, please share it with others and subscribe to the blog to support me, and receive a bi-monthly-ish e-mail notification on my latest articles.