livewire/livewire: [4.x] Don't include redirect responses in the returns effect
Contributed by lazerg
Part of the livewire v4.4.7 release
Livewire 4.x 改变了 redirect() 辅助函数在底层的运作方式:PR #10555 使得 Redirector::to() 返回一个真正的 Symfony RedirectResponse,而不是重定向器实例本身。这是一个很好的清理,但它暴露了 Livewire 捕获动作返回值方式上的一个空白:任何返回 redirect()->route(...) 的动作都会突然将一个完整的响应对象放入 effects.returns 负载中。对该对象进行 JSON 编码可能会抛出 InvalidArgumentException: Type is not supported,将一个完全成功的动作变成 500 错误。PR #10596 通过赋予重定向响应与下载已经获得的相同处理来填补了这一空白:它们在 returns 中被置空,并且仅通过它们专用的效果(effect)进行传输。如果您正从 v3.8.10 升级,并且在 4.x 中遇到了重定向动作出现神秘 500 错误的情况,此修复程序可以解决该问题,而无需您修改任何代码。
早期 4.x 版本中的故障
当 Livewire 动作返回一个值时,Livewire 会将其存储在 effects.returns 中,以便前端可以在往返后访问它。从 #10555 开始,return redirect()->route('dashboard') 会返回一个真正的 Symfony\Component\HttpFoundation\RedirectResponse —— 而该对象才是最终出现在 returns 中的内容。
这有两方面的问题。首先,它是冗余的:重定向已经通过 redirect 效果到达浏览器,这是前端需要的唯一通道。其次,这很脆弱:RedirectResponse 不保证是可 JSON 编码的。根据响应所携带的内容,对负载进行编码会失败,并抛出 InvalidArgumentException: Type is not supported,并且更新请求会返回 500 —— 即使动作本身已成功运行到底。Issue #10595 正是关于该报告。
修复:像处理下载一样处理重定向
Livewire 出于同样的原因已经对另外两种响应类型进行了特殊处理。StreamedResponse 和 BinaryFileResponse(文件下载)通过 download 效果到达浏览器,因此 HandleComponents 中的 callMethods() 会将它们在 returns 中替换为 null —— 从而保留了它们在数组中的位置,因为 returns[i] 必须与批次中第 i 个动作调用相匹配。此 PR 在早期返回路径和正常路径中,都将 RedirectResponse 添加到了相同的检查中:
// 文件下载和重定向通过它们自己的效果发送到浏览器,因此我们
// 不应该在此处将响应对象添加为返回值。但我们需要保留其位置
// 在 `returns` 中,所以我们只需将其设置为 `null` 即可...
if ($return instanceof StreamedResponse || $return instanceof BinaryFileResponse || $return instanceof RedirectResponse) {
$return = null;
}
一个新的回归测试 test_redirect_response_is_not_included_in_returns 锁定了这一行为:运行一个调用重定向辅助函数的动作必须在 effects.returns 中的动作位置产生 null。
在负载中看到的变化
以一个典型的保存后重定向为例:
public function publish()
{
$post = Post::create($this->validate());
return redirect()->route('posts.show', ['post' => $post]);
}
在损坏的构建版本中,响应对象会被序列化到负载中 —— 或者导致编码直接失败。修复后,负载是干净且可预测的:
{
"effects": {
"returns": [null],
"redirect": "https://example.test/posts/42"
}
}
这里的 null 并不是信息的丢失。前端执行重定向所需的一切内容都存在于 effects.redirect 中;returns 只是保持其位置占位符,以便批量操作调用保持对齐。
升级影响
- 如果您在迁移到 4.x 后遇到重定向动作出现
InvalidArgumentException: Type is not supported(或一般的 500 错误),此修复程序可以解决该问题。不需要更改应用程序代码。 return redirect()->...、->with(...)闪存数据、命名路由和away()重定向都继续像以前一样工作 ——redirect效果未受影响。- 唯一可观察到的变化是在负载中:重定向动作现在为
effects.returns贡献了null,而不是响应对象 —— 这与下载多年来遵循的约定相同。 - 如果您有检查
effects.returns的自定义 JavaScript 或测试工具,现在应该期望在重定向动作的位置得到null。
核心要点
- 早期的 4.x 版本在返回
redirect()->route(...)的动作上可能会报 500 错误,因为原始的RedirectResponse被序列化到了effects.returns中(#10555 的回归,报告为 #10595)。 - #10596 在
returns中将RedirectResponse置空,与对流式和文件下载响应的现有处理相匹配,并且位置槽被保留。 - 重定向仍然完全通过
redirect效果起作用;您的应用程序代码没有任何变化。 - 只有读取
effects.returns的自定义工具才需要考虑到null。