Framework: Laravel using a GET route for a POST request

Created on 7 Oct 2016  路  5Comments  路  Source: laravel/framework

  • Laravel Version: 5.3
  • PHP Version: 7.0.0
  • Database Driver & Version: sqlite + mysql

I've never seen anything like this, but I have a routes file that works just fine when you're using the dev site in Chrome, but fails to distinguish between these two routes using Postman or Codeception:

 POST   conference/attendees/{token}   AttendeeController@updateByToken             
 GET|HEAD   conference/attendees/{token}   AttendeeController@showByToken   

When I do a post request in testing or in Postman, the GET function is used. However, if I comment out either the POST or the GET route in the routes file, the request then fails completely. Any idea how this could be?

Also, if I turn the POST request into an anonymous function, I see the function invoked correctly. I've verified that the names of the functions in AttendeeController are correct.

UPDATE: For whatever reason, removing the custom form request allows the correct function to process the POST request, which I have verified is, in fact, a POST. The correct errors from a failed request are returned to the dev site in Chrome but in the case of Postman and Codeception, the function assigned to the GET route handles the request in error.

UPDATE AGAIN: I removed the custom form request and it worked. I then replaced that form request with another and when authorized was set to false, it returned Forbidden as expected. As soon as I added a rule 'email' => 'required' it immediately went back to forwarding the request to the GET route rather than returning the custom error messages.

UPDATE AGAIN AGAIN: Based on a conversation in Slack I tried changing the name of the endpoints to be different. Here's what is happening:

  1. A POST request comes in and is initially handled by the correct function.
  2. The form request in that function checks for the necessary inputs and, if it doesn't find something it needs, it redirects the request to the GET route rather than returning 422 unprocessable like I would expect.
  3. Moving to manual validation inside the function handling the POST request makes everything work again.

For whatever reason, it appears that the form request is causing the issue by not returning the correct status code and, somehow, trying to use a route of the same name but as a GET request. This is all now above my head, just reporting what I'm seeing.

Most helpful comment

Ok, after a week of working on this, @janhenkgerritsen saved the day (again). By digging through the code I was able to find that if I added this function to /app/Http/Requests/Request.php Laravel treated all of my requests as AJAX:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

abstract class Request extends FormRequest
{

    /**
     * Treat every single request as AJAX
     *
     * @return bool
     */
    public function ajax()
    {
        return true;
    }

}

Hopefully this will be useful to someone else -- please feel free to mark this as closed.

All 5 comments

For postman, you need to change request type to POST if you want to check post request.

Of course, that has been done. The issue is that when a form request fails Laravel is looking for a different route rather than returning an "unprocessable".

@mcblum You probably have not set a value for the $redirect, $redirectRoute or $redirectAction property of your FormRequest object.

You can find these properties in the Illuminate\Foundation\HttpFormRequest class.

If validation fails the failedValidation method of this class is called, which throws a ValdationException with a response that is generated in the response method of the class. This response is a redirect, and the URL for this redirect is determined by a call to the getRedirectUrl method of the FormRequest class. If none of the properties I mentioned above are set this method falls back to the previous method of the Illuminate\Routing\UrlGenerator class:

https://github.com/laravel/framework/blob/5.3/src/Illuminate/Routing/UrlGenerator.php#L136

And here the URL of your POST request is returned, resulting in a redirect to the GET route for this URL.

@janhenkgerritsen Woah. Is this new in 5.3? I have never in my life set the $redirect, $redirectRoute or $redirectAction variables in a form request. Before, any failures would simply return the errors as JSON. How do I get it to do that again?

Thank you for the explanation.

Edit: Found this in the docs:

In this example, we used a traditional form to send data to the application. However, many applications use AJAX requests. When using the validate method during an AJAX request, Laravel will not generate a redirect response. Instead, Laravel generates a JSON response containing all of the validation errors. This JSON response will be sent with a 422 HTTP status code.

If that isn't happening, could it be that it doesn't see the Postman and Codeception requests as AJAX? If that's true, is there any way to tell my code to universally treat requests as AJAX?

Ok, after a week of working on this, @janhenkgerritsen saved the day (again). By digging through the code I was able to find that if I added this function to /app/Http/Requests/Request.php Laravel treated all of my requests as AJAX:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

abstract class Request extends FormRequest
{

    /**
     * Treat every single request as AJAX
     *
     * @return bool
     */
    public function ajax()
    {
        return true;
    }

}

Hopefully this will be useful to someone else -- please feel free to mark this as closed.

Was this page helpful?
0 / 5 - 0 ratings