laravel / framework

The Laravel Framework.
https://laravel.com
MIT License
32.43k stars 10.99k forks source link

Inconsistent redirect behavior #13892

Closed Schaumbaum closed 8 years ago

Schaumbaum commented 8 years ago

When using a "native" HTTP PUT request (eg using CURL) I will be redirected to the main page (/) with a PUT request, while if using a HTTP POST request with a "_method=put" parameter (which is resolved as a PUT request within the Laravel framework) I'm redirected with a GET request, which in my opinion should be the behavior also with a "native" request. I'm using the Laravel Framework Version 5.2.35.

routes.php

    // request target
    $router->put('check', function (\Illuminate\Http\Request $request) {
        return redirect('/');
    });

    // redirect targets
    $router->get('/', function (\Illuminate\Http\Request $request) {
        echo 'GET redirect';
    });

    $router->post('/', function (\Illuminate\Http\Request $request) {
        echo 'POST redirect';
    });

    $router->put('/', function (\Illuminate\Http\Request $request) {
        echo 'PUT redirect';
    });

requests

// Curl POST request
 curl -L  --data "_method=PUT" http://mydomain/check
// GET redirect

// another Curl POST request
 curl -L -X POST --data "_method=PUT" http://mydomain/check
// POST redirect

// Curl PUT request
curl -L -X PUT http://mydomain/check
// PUT redirect

Addition: Checking the incoming curl requests with $request->method(); shows that all are resolved as PUT requests, but the redirect differs for every request.

JABirchall commented 8 years ago

I believe _method is only read as a pseudo request if it is sent in a post request.

See the docs here: https://laravel.com/docs/5.2/routing#form-method-spoofing

Schaumbaum commented 8 years ago

Thanks for your remark. I have checked this and it is like you said, the "_method" parameter seems only to be resolved for POST requests. I have updated my question regarding to this. But now Laravels behavior looks even more Inconsistent to me than before. One POST request is redirected with POST and the other with GET.

JABirchall commented 8 years ago

Have you tried it without the post request and just the put request? Does laravel read the post with _method=put and trigger the put route?

Schaumbaum commented 8 years ago

All three requests trigger the PUT route (/), but then they are redirected to different routes. When checking the used method with $request->method(); all three are resolved as PUT requests (before the redirect).

JABirchall commented 8 years ago

Hmmm, Mite be better to hear something from @GrahamCampbell or @taylorotwell see if this is intended behaviour or not

sisve commented 8 years ago

The method spoofing with _method is only enabled for http posts since it's a workaround for browser forms only supporting get and post. A client able to send put/delete requests directly has no need for it.

The docs for curl 7.43.0 states...

-L, --location (HTTP/HTTPS) If the server reports that the requested page has moved to a different location (indicated with a Location: header and a 3XX response code), this option will make curl redo the request on the new place. If used together with -i, --include or -I, --head, headers from all requested pages will be shown. When authentication is used, curl only sends its credentials to the initial host. If a redirect takes curl to a different host, it won't be able to intercept the user+password. See also --location-trusted on how to change this. You can limit the amount of redirects to follow by using the --max-redirs option.

When curl follows a redirect and the request is not a plain GET (for example POST or PUT), it will do the following request with a GET if the HTTP response was 301, 302, or 303. If the response code was any other 3xx code, curl will re-send the following request using the same unmodified method.

[...]

-X, --request (HTTP) Specifies a custom request method to use when communicating with the HTTP server. The specified request method will be used instead of the method otherwise used (which defaults to GET). Read the HTTP 1.1 specification for details and explanations. Common additional HTTP requests include PUT and DELETE, but related technologies like WebDAV offers PROPFIND, COPY, MOVE and more.

Normally you don't need this option. All sorts of GET, HEAD, POST and PUT requests are rather invoked by using dedicated command line options.

This option only changes the actual word used in the HTTP request, it does not alter the way curl behaves. So for example if you want to make a proper HEAD request, using -X HEAD will not suffice. You need to use the -I, --head option.

The method string you set with -X will be used for all requests, which if you for example use -L, --location may cause unintended side-effects when curl doesn't change request method according to the HTTP 30x response codes - and similar.

[...]

  1. `curl -L --data="_method="PUT"`` Issues a POST (since you've sending data), Laravel sees a PUT, and curl uses a GET for redirection.
  2. curl -L -X POST --data "_method=PUT" Same as above, but you force the redirection to use POST.
  3. curl -L -X PUT Issues a PUT, and forces a PUT for redirection.

Basically, you're telling curl which http method to use for the redirect with -X, and not just the initial request. This isn't a Laravel issue, but just the way you tell curl to behave.

Schaumbaum commented 8 years ago

Thanks for your great answer. This totally makes sense. I haven't found any differences at Laravels redirect responses, because there weren't any. I was absolutely mislead, because the HTTP client (HttpRequester) I used before had the same behavior (maybe it uses curl under the hood). With this new knowledge I have checked more HTTP clients and some (for example Postman) are following the redirect (as expected by me) with a GET method. My conclusion is to be very careful with redirects within an API/Webservice context. Laravel is behaving correct. Therefore this issue can be closed.