How to force FormRequest return json in Laravel 5.1?
Solution 1
It boggles my mind why this is so hard to do in Laravel. In the end, based on your idea to override the Request class, I came up with this.
app/Http/Requests/ApiRequest.php
<?php
namespace App\Http\Requests;
class ApiRequest extends Request
{
public function wantsJson()
{
return true;
}
}
Then, in every controller just pass \App\Http\Requests\ApiRequest
public function index(ApiRequest $request)
Solution 2
I know this post is kind of old but I just made a Middleware that replaces the "Accept" header of the request with "application/json". This makes the wantsJson()
function return true
when used. (This was tested in Laravel 5.2 but I think it works the same in 5.1)
Here's how you implement that :
-
Create the file
app/Http/Middleware/Jsonify.php
namespace App\Http\Middleware; use Closure; class Jsonify { /** * Change the Request headers to accept "application/json" first * in order to make the wantsJson() function return true * * @param \Illuminate\Http\Request $request * @param \Closure $next * * @return mixed */ public function handle($request, Closure $next) { $request->headers->set('Accept', 'application/json'); return $next($request); } }
-
Add the middleware to your
$routeMiddleware
table of yourapp/Http/Kernel.php
fileprotected $routeMiddleware = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'jsonify' => \App\Http\Middleware\Jsonify::class ];
-
Finally use it in your
routes.php
as you would with any middleware. In my case it looks like this :Route::group(['prefix' => 'api/v1', 'middleware' => ['jsonify']], function() { // Routes });
Solution 3
Based on ZeroOne's response, if you're using Form Request validation you can override the failedValidation method to always return json in case of validation failure.
The good thing about this solution, is that you're not overriding all the responses to return json, but just the validation failures. So for all the other Php exceptions you'll still see the friendly Laravel error page.
namespace App\Http\Requests;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException;
use Symfony\Component\HttpFoundation\Response;
class InventoryRequest extends FormRequest
{
protected function failedValidation(Validator $validator)
{
throw new HttpResponseException(response($validator->errors(), Response::HTTP_UNPROCESSABLE_ENTITY));
}
}
Solution 4
if your request has either X-Request-With: XMLHttpRequest header or accept content type as application/json FormRequest will automatically return a json response containing the errors with a status of 422.
Chung
2010 ~ 2016: Software developer 2016/10: Established a new company called Haposoft to provide Software Offshore Development Service in Viet Nam
Updated on June 03, 2022Comments
-
Chung almost 2 years
I'm using FormRequest to validate from which is sent in an API call from my smartphone app. So, I want FormRequest alway return json when validation fail.
I saw the following source code of Laravel framework, the default behaviour of FormRequest is return json if reqeust is Ajax or wantJson.
//Illuminate\Foundation\Http\FormRequest class /** * Get the proper failed validation response for the request. * * @param array $errors * @return \Symfony\Component\HttpFoundation\Response */ public function response(array $errors) { if ($this->ajax() || $this->wantsJson()) { return new JsonResponse($errors, 422); } return $this->redirector->to($this->getRedirectUrl()) ->withInput($this->except($this->dontFlash)) ->withErrors($errors, $this->errorBag); }
I knew that I can add
Accept= application/json
in request header. FormRequest will return json. But I want to provide an easier way to request my API by support json in default without setting any header. So, I tried to find some options to force FormRequest response json inIlluminate\Foundation\Http\FormRequest
class. But I didn't find any options which are supported in default.Solution 1 : Overwrite Request Abstract Class
I tried to overwrite my application request abstract class like followings:
<?php namespace Laravel5Cg\Http\Requests; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Http\JsonResponse; abstract class Request extends FormRequest { /** * Force response json type when validation fails * @var bool */ protected $forceJsonResponse = false; /** * Get the proper failed validation response for the request. * * @param array $errors * @return \Symfony\Component\HttpFoundation\Response */ public function response(array $errors) { if ($this->forceJsonResponse || $this->ajax() || $this->wantsJson()) { return new JsonResponse($errors, 422); } return $this->redirector->to($this->getRedirectUrl()) ->withInput($this->except($this->dontFlash)) ->withErrors($errors, $this->errorBag); } }
I added
protected $forceJsonResponse = false;
to setting if we need to force response json or not. And, in each FormRequest which is extends from Request abstract class. I set that option.Eg: I made an StoreBlogPostRequest and set
$forceJsoResponse=true
for this FormRequest and make it response json.<?php namespace Laravel5Cg\Http\Requests; use Laravel5Cg\Http\Requests\Request; class StoreBlogPostRequest extends Request { /** * Force response json type when validation fails * @var bool */ protected $forceJsonResponse = true; /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ]; } }
Solution 2: Add an Middleware and force change request header
I build a middleware like followings:
namespace Laravel5Cg\Http\Middleware; use Closure; use Symfony\Component\HttpFoundation\HeaderBag; class AddJsonAcceptHeader { /** * Add Json HTTP_ACCEPT header for an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { $request->server->set('HTTP_ACCEPT', 'application/json'); $request->headers = new HeaderBag($request->server->getHeaders()); return $next($request); } }
It 's work. But I wonder is this solutions good? And are there any Laravel Way to help me in this situation ?
-
James about 8 yearsI'm having the same issue and thought I'd try this solution. The problem I'm having with this is that by the time my controller method loads my instance of FormRequest implicitly, its not the same request instance from the one modified in the Jsonify middleware, so wantsJson is effectively "reset" to false.
-
SimonDepelchin about 8 yearsFormRequest extends Request so it should be the same instance, maybe show some code
-
Chung about 8 years@SimonDepelchin This solution is like the Solution 2 which I mentioned in the question.
-
SimonDepelchin about 8 yearsYes it is, with more details and more in the "Laravel way" imho.
-
Ryan over 7 yearsThis solution is better as it returns everything as JSON. If you make an unauthorised request with
ApiRequest
it will give back a 404 html page, however this will return a 401 Unauthorised JSON error. -
Squiggs. over 7 yearsAccept: application/json in your header may also help with this.
-
craig_h over 6 yearsI was coming across this problem while testing a 5.4 app. Simply adding this method to a form request does the trick.
-
Wesley Smith over 6 years"It boggles my mind how hard this is"......."here's a super easy way to do it", lol, that made me laugh. +1
-
Finesse over 6 yearsIt doesn't work: such request has empty
$request->all()
-
Mei Gwilym about 6 yearsL5.5: I added
\App\Http\Middleware\Jsonify::class
to the api middleware group in app/Http/Kernel.php so it applies to all api requests. -
Tarek Adam about 6 yearsThis works in the more binary sense of either working or not working, but it's a real lack-luster solution considering how well drafted the question was.
-
Syclone almost 6 yearsI am still getting HTML response for unauthorized requests instead of JSON
-
ManojKiran Appathurai over 4 yearscan you share the fully Classified namespace of
Response
class -
ZeroOne over 4 years@Manojkiran.A Illuminate\Support\Facades\Response.
-
T30 over 4 yearsDoesn't work in my Laravel 5.8, still returning 404 when the request header is not 'Application/json'.
-
T30 over 4 yearsThe problem with this solution is that you lose the friendly Laravel error page for every other exception: all the Php errors will be shown in json format from now on!
-
SimonDepelchin over 4 yearsThat's only the case if you apply the
jsonify
middleware to all your routes. In the solution I create a specific group for the API, so the other routes are not impacted. -
Odin Thunder about 4 yearsClear solution, thanks a lot, work fine on Laravel 6.14
-
Max over 2 yearsI have a this problem in Laravel 8 and this methode work for me. Thanks T30