Another I-did-not-sleep-enough question, I'm sure. I'm posting it as a sacrifice to the Elder God Murphy: as soon as I expose my moronity for all to see, I'm guaranteed to find by myself that answer that otherwise will elude me for hours (by way of further penance, I will then post the answer as well).
I have a HTML form that gets rendered as
<form method="post" id="mysearch" action="/search/?uid=1701">
<input id="searchterm" type="text" name="query" />
</form>
The form can be submitted via jQuery $.POST
with url of '/search' and data of { uid: '1701', query: $('#searchterm').val() }
and it works.
If I press ENTER after entering something, and thus override the jQuery submission, the following happens:
Route::post('/search', function() {...
does not get invoked.The 301 response looks like something by Laravel4, added explicitly:
HTTP/1.0 301 Moved Permanently
Date: Thu, 28 Nov 2013 14:05:29 GMT
Server: Apache
X-Powered-By: PHP/5.4.20
Cache-Control: no-cache
Location: http://development/search?uid=1701
Connection: close
Content-Type: text/html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="refresh" content="1;url=http://development/search?uid=1701" />
<title>Redirecting to http://development/search?uid=1701</title>
</head>
<body>
Redirecting to <a href="Redirecting to http://development/search?uid=1701">Redirecting to http://development/search?uid=1701</a>
</body>
</html>
This is not the same as this question, because there the redirect is expected and it's the answer to that which is undesired. Here it's the redirect itself that is generated for no reason that I can (for now) see.
I suspect that for some reason I'm triggering the "security redirect" described in this other answer, that is not triggered by jQuery (either because it puts everything in the POST while here I've one parameter in the URL and another in the POST, or because jQuery uses XHR).
I had thought it might be a CSRF defense, but that particular route isn't shielded. As a last resource, I'll CSRF-protect the route and add the token to the form, even if it looks a bit like voodoo to me. Something vaguely similar appears to be happening in Rails.
I've got not one, not two, but three workarounds that neatly sidestep the question of why is the above happening:
keyUp
event in the form.$('#search-button').click()
...but I'd like to make without the button altogether (which I could do with jQuery) and without jQuery altogether. As well as understand what is happening here. I'm 99% certain I'm missing something obvious.
I'm now going to grep -r "Redirecting to" *
the whole framework source code (I expect to find something in Symfony/Components/HttpFoundation/ResponseRedirect
) and doing step-by-step from there.
ensure POST URLs do not end with a slash. -- but Laravel 4.1 users check the update below, first.
=======
When it works, it's not superstition - it's science :-(
as soon as I expose my moronity for all to see, I'm guaranteed to find by myself that answer that would otherwise elude me for hours
I will argue that Laravel4's HTML message could have been just a bit more informative.
The grep
found as expected the origin of the redirect:
grep -r "Redirecting to" *
vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/RedirectResponse.php: <title>Redirecting to %1$s</title>
vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/RedirectResponse.php: Redirecting to <a href="%1$s">%1$s</a>.
at that point, a simple backtrace found the origin, very early in Laravel4 spin-up:
bootstrap/start.php:
...
| The first thing we will do is create a new Laravel application instance
| which serves as the "glue" for all the components of Laravel, and is
| the IoC container for the system binding all of the various parts.
|
*/
$app = new Illuminate\Foundation\Application;
$app->redirectIfTrailingSlash();
As soon as I saw the redirectIfTrailingSlash
, I realized that the two URLs sent by the form and by jQuery were not the same:
... action="/search/?uid=1701"> <--- TRAILING SLASH AFTER 'search'
... url: '/search', <--- NO TRAILING SLASH
data: {
uid : 1701,
query: $('#searchterm').val()
},
...
Why this should happen I don't quite grok, but the solution is fiendishly simple:
remove the slash from the POST action field.
(and ensure .htaccess
has no rule to consider the URL a "directory" and add back the slash - but if it had, jQuery would have failed too).
Apparently, the matter got reviewed in Laravel 4.1. The upgrade mentions
Removing Redirect Trailing Slash
In your bootstrap/start.php file, remove the call to $app->redirectIfTrailingSlash(). This method is no longer needed as this functionality is now handled by the .htaccess file included with the framework.
Next, replace your Apache .htaccess file with this new one that handles trailing slashes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With