Phil Cross
Phil Cross

Reputation: 9302

Laravel Ajax validation

I understand how to validate requests by type-hinting the class name in the controller method. However for Ajax requests, According to the documentation, I should validate data in the controller, because using a validator class will redirect rather than send a response.

The main part I'm looking at is this:

If the incoming request was an AJAX request, no redirect will be generated. Instead, an HTTP response with a 422 status code will be returned to the browser containing a JSON representation of the validation errors.

However, my controller is as follows:

public function update(App\Permission $permission, Request $request)
{
    $this->validate($request, [
        'permission_description' => 'required|string'
    ]);

    ...
}

And I can't for the life of me get it to respond with JSON. The documentation states that if it fails, it throws an Illuminate\Contracts\Validation\ValidationException exception, but I can't catch it.

Whenever it fails, it always redirects back to the edit page. Obviously I don't want this, I want the json response.

I have just tried "manually writing it out" with the whole $v = Validator::make($request->all(), ...); which does work, but what's the point in using the $this->validate() way if it doesn't work?

Does the $this->validate() method just not work with AJAX and I have to write it the long way each time? Am I doing something wrong?!

Below is what I've tried:

public function update(App\Permission $permission, UpdatePermissionRequest $request)
{
   /** Redirects rather than returns JSON if the validation fails **/
}

----------------------------------

public function update(App\Permission $permission, Request $request)
{
    $this->validate($request, [
        'permission_description' => 'required|string'
    ]);

    /** AND I've also tried: **/

    try {
        $this->validate($request, ['permission_description' => 'required|string']);
    } catch (\Illuminate\Contracts\Validation\ValidationException $e {
        echo $e; /** Echoing for debug reasons **/
        exit;
    }

    ... 
    /** Still redirects the browser, even if it is an AJAX request **/
}

-----------------------------------------

use Validator;
...
public function update(App\Permission $permission, Request $request)
{
    $v = Validator::make($request->all(), [
        'permission_description' => 'required|string'
    ]);

    if($v->fails())
    {
        return response()->json(['reply' => false]);
    }

    /** Works **/
}

UPDATE The documentation is incorrect. It states that the $this->validate() method throws a Illuminate\Contracts\Validation\ValidationException but it doesn't. It throws a Illuminate\Http\Exception\HttpResponseException exception.

Upvotes: 2

Views: 8328

Answers (3)

Filip Holmberg
Filip Holmberg

Reputation: 51

Simply telling that you want json in the header should also fix this. Laravel checks if the request is ajax of if json is requested.

if ($this->ajax() || $this->wantsJson())
{
    return new JsonResponse($errors, 422);
}

Solution:

Add header

Accept: application/json

Upvotes: 5

Laurence
Laurence

Reputation: 60040

Your statement that the docs say it is best to validate AJAX requests in the controller is simply incorrect.

If you scroll a bit further down from what you linked - you'll see this under the FormValidation section

If validation fails, a redirect response will be generated to send the user back to their previous location. The errors will also be flashed to the session so they are available for display. If the request was an AJAX request, a HTTP response with a 422 status code will be returned to the user including a JSON representation of the validation errors.

In other words - there is no reason you cannot do this in a simple FormRequest and simply your code significantly. It will automatically handle the fact it is an AJAX call and return the appropriate HTTP responses.

I do this all the time in my L5 apps - it works flawlessly.

Upvotes: 2

Phil Cross
Phil Cross

Reputation: 9302

Ok, so it looks like there were 2 contributing factors.

  1. The reason it was redirecting instead of responding with JSON is because the X-Requested-With field wasn't set on the AJAX request. This the AJAX wrapper for the application was setup to deal with Cross Domain requests, which seems to strip out the X-Requested-With field, which makes the ultimate request look non-ajax to the server - hence the redirection.

  2. The reason why it wasn't catching the Illuminate\Contracts\Validation\ValidationException exception is because that exception is not thrown. If you open up Illuminate\Foundation\Validation\ValidatesRequest.php, and search for the function throwValidationException(), it actually throws a HttpResponseException instead. I attempted to catch the HttpResponseException and it was caught successfully - I assume the documentation is wrong.

The solution was to either remove the cross domain attributes on the ajax request, add the X-Requested-With header manually, as per the answer in this post. This would make the application see that it was an AJAX request.

And if you wanted to manually catch the exception, you need to catch the HttpResponseException, not the ValidationException.

Upvotes: 2

Related Questions