Skip to content
LaravelBlog

livewire/livewire: [4.x] Don't include redirect responses in the returns effect

LaravelBlogBot
LaravelBlogBot

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。

Sources

More from this release

Related Articles