Laravel4 POST 无法解释的重定向到 GET
另一个我没睡够的问题,我敢肯定.我将它作为祭品发布给墨菲老神:一旦我将我的愚蠢暴露给所有人看,我保证自己找到答案,否则我会躲避几个小时(作为进一步的忏悔,我也会发布答案).
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).
我有一个呈现为的 HTML 表单
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>
表单可以通过jQuery提交$.POST
,url为'/search',数据为{ uid: '1701', query: $('#searchterm').val() }
它可以工作.
The form can be submitted via jQuery $.POST
with url of '/search' and data of { uid: '1701', query: $('#searchterm').val() }
and it works.
如果我在输入内容后按 ENTER,从而覆盖 jQuery 提交,则会发生以下情况:
If I press ENTER after entering something, and thus override the jQuery submission, the following happens:
- 按预期向服务器发出 POST.
Route::post('/search', function() {...
确实不被调用.- 返回 301 Moved Permanently
- 一个 GET 搜索参数丢失 发送到重定向指定的 URL
- 很明显,搜索失败了.
301 响应看起来像 Laravel4 的东西,明确添加:
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>
这与这个问题不同,因为那里的重定向是预期的,这是不希望的 that 的答案.这是重定向本身,它是无缘无故生成的,我(现在)可以看到.
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.
我怀疑由于某种原因我触发了 this other answer,不是由 jQuery 触发的(或者是因为它将所有内容都放在 POST 中,而这里我在 URL 中有一个参数,在 POST 中有另一个参数,或者因为 jQuery 使用 XHR).
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).
我原以为这可能是 CSRF 防御,但那条特定的路线并没有被屏蔽.作为最后一个资源,我将对路由进行 CSRF 保护并将令牌添加到表单中,即使它在我看来有点像巫毒教.Rails 中似乎正在发生类似的事情.
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
事件. - 将表单的提交事件重定向到jQuery
- (最透明)将上述事件路由到
$('#search-button').click()
- (most brutal) block the
keyUp
event in the form. - redirect the submit event of the form to jQuery
- (most transparent) route the above event to
$('#search-button').click()
...但我想完全不使用按钮(我可以使用 jQuery)和 完全不使用 jQuery.以及了解这里发生了什么.我 99% 确定我遗漏了一些明显的东西.
...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.
我现在要去 grep -r "Redirecting to" *
整个框架源代码(我希望在 Symfony/Components/HttpFoundation/ResponseRedirect
) 并从那里逐步进行.
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.
推荐答案
TL;DR
确保 POST URL 不以斜杠结尾. -- 但 Laravel 4.1 用户首先检查下面的更新.
TL;DR
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
我认为 Laravel4 的 HTML 消息可能会提供更多信息.
I will argue that Laravel4's HTML message could have been just a bit more informative.
grep
按预期找到了重定向的来源:
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>.
那时,在 Laravel4 启动的早期,一个简单的回溯就找到了原点:
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 IlluminateFoundationApplication;
$app->redirectIfTrailingSlash();
一看到redirectIfTrailingSlash
,我就意识到form和jQuery发送的两个url不一样:
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:
从 POST 操作字段中删除斜线.
(并确保 .htaccess
没有规则将 URL 视为目录"并 添加斜杠 - 但如果有,jQuery 也会失败).
(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).
显然,这件事在 Laravel 4.1 中得到了审查.upgrade 提到
Apparently, the matter got reviewed in Laravel 4.1. The upgrade mentions
删除重定向尾部斜杠
在您的 bootstrap/start.php 文件中,删除对$app->redirectIfTrailingSlash().不再需要此方法,因为此功能现在由包含的 .htaccess 文件处理框架.
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.
接下来,用这个新文件替换您的 Apache .htaccess 文件处理尾部斜杠.
Next, replace your Apache .htaccess file with this new one that handles trailing slashes.
相关文章