在rails中,Turbo只是将服务器的响应附加到当前页面的末尾

JUl*_*der 5 ruby-on-rails hotwire-rails turbo-frames ruby-on-rails-7

问题:Turbo 只是将服务器的响应附加到当前页面的末尾。没有像预期那样替换页面。

最近从 Rails 6 升级到 7。我还从 importmap 切换到 esbuild。

我有一个正在向创建方法发布帖子的表单。控制器响应

  if @stamp.save       
        redirect_to stamps_path, notice: "Input saved"
      else
Run Code Online (Sandbox Code Playgroud)

发生的奇怪的事情是响应从顶部附加到源代码的底部。因此,在原始源代码的末尾附加了响应中的新头部部分,如下所示

<...> The whole initial html <...>
</body>
<title>Page title</title> <-- The server response gets appended after </body>
<meta .... the whole page gets repeated again
Run Code Online (Sandbox Code Playgroud)

不太确定要在这里向您展示什么,一直在研究所有设置等。我从未见过 Turbo 有这样的响应行为。

source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby "3.1.2"
gem "rails", "~> 7.0.2", ">= 7.0.2.4"
gem "sprockets-rails"
gem "puma", "~> 5.6"
gem "haml-rails", "~> 2.0"
gem "sqlite3", "~> 1.4"
gem 'devise', '~> 4.8'
gem 'devise-i18n', '~> 1.10'
gem "turbo-rails"
gem "jsbundling-rails", "~> 1.0"
gem 'sass-rails'
gem "stimulus-rails"
gem 'rubyXL', '~> 3.3'
gem "jbuilder"
gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ]
gem 'kaminari', '~> 1' # pagination gem
gem "bootsnap", require: false

group :development, :test do
  gem "debug", platforms: %i[ mri mingw x64_mingw ]
end

group :development do
  gem "web-console"
end

group :test do
  gem "capybara"
  gem "selenium-webdriver"
  gem "webdrivers"
end

Run Code Online (Sandbox Code Playgroud)
{
  "private": true,
  "license": "ISC",
  "name": "name",
  "version": "0.1.0",
  "scripts": {
    "release": "npm run build && npm run publish",
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=assets"
  },
  "dependencies": {
    "@fortawesome/fontawesome-svg-core": "^6.1.2",
    "@fortawesome/free-brands-svg-icons": "^6.2.0",
    "@fortawesome/free-solid-svg-icons": "^6.1.2",
    "@hotwired/stimulus": "^3.1.0",
    "@hotwired/turbo-rails": "^7.2.5",
    "@popperjs/core": "^2.11.5",
    "@rails/activestorage": "^6.0.0",
    "bootstrap": "^5.2.0",
    "esbuild": "^0.17.7"
  }
}

Run Code Online (Sandbox Code Playgroud)

我一直在尝试复制另一个使用 Turbo 的 Rails 应用程序。一直在研究所有设置等。其他页面可以采用redirect_to并按预期替换内容。我正在使用 haml,所以 HTML 结构应该没问题。

Ale*_*lex 10

我只能想到一种可能发生这种情况的方法:你有show.erb模板。只需将其重命名为show.html.erb(或.haml.slim,没关系)。


默认情况下,表单以TURBO_STREAM形式提交,这是Rails在日志中显示的内容,但这并不是全部。表单提交请求设置 Accept标头:

Accept: text/vnd.turbo-stream.html, text/html, application/xhtml+xml
#       ^
# here is where `TURBO_STREAM` comes from
Run Code Online (Sandbox Code Playgroud)

这些是Turbo期望收到的Content-Type响应。当您在控制器操作中使用块时,标头决定了要调用哪个块:respond_toAcceptformat

respond_to do |format|
  if @model.save(model_params)
    # if you have turbo_stream, it will run first
    # format.turbo_stream { render turbo_stream: turbo_stream.replace(model) }
    # if you don't have turbo_stream, next format in `Accept` header is html
    format.html { redirect_to model_url(@model) }
    format.json { render :show, status: :ok, location: @model }
    # if nothing matches, you get `ActionController::UnknownFormat` error.
    # the one you get when trying to render `format.js`, but Turbo 
    # doesn't handle js format, so it doesn't send `Accept: text/javascript`
  else
    format.html { render :edit, status: :unprocessable_entity }
    format.json { render json: @model.errors, status: :unprocessable_entity }
  end
end
Run Code Online (Sandbox Code Playgroud)

当你使用respond_toblock时,rails会自动设置一些渲染选项,其中之一就是:content_type依次设置Content-Type响应头。因此,如果format.html块运行,它会设置Content-Type: text/html哪个Turbo知道如何通过重定向或替换页面内容来处理。

重要的是我们从Turbo_stream格式转变为html。不用说,html需要渲染 html 响应,并且turbo_stream必须是一个<turbo-stream>标签。

当你不使用respond_toblock 时会发生什么?Rails将尝试渲染与格式匹配的模板。例如,turbo_stream请求将查找show.turbo_stream.erbthen show.html.erbthen show.erb,如下所示:

render :show, formats: [:turbo_stream, :html]
Run Code Online (Sandbox Code Playgroud)

假设它找到show.html.erb,因为存在html扩展,rails会将响应Content-Type设置为text/html,并且一切按预期工作。

但是,如果找到show.erb,则没有任何信息告诉rails它是html格式,我们没有使用respond_to块,因此未显式设置响应Content-Type,剩下的唯一选择是回退到第一个Accept类型,即涡轮流。现在您正在渲染html模板,但响应类型为Turbo_stream,并且没有<turbo-stream>标签。

turbo看到turbo_stream响应时,它会将其附加到文档中,这就是您所看到的。由于没有<turbo-stream>指令,因此它保持不变。


长话短说,一路上需要设置正确的内容类型。

要么使用respond_to块来设置必要的渲染选项。或者自己设置渲染选项:content_type: "text/html". 或者通过使用和扩展让Rails了解内容类型。html.erbturbo_stream.erb

请注意,当您重定向时,turbo_stream内容类型将保留,因为Turbo在前端处理重定向。如果显示操作中没有html.erb扩展名和块,则会出现内容类型不匹配的情况。respond_to

  • 天哪,我爱你..这是解决方案,也是我能得到的最好的教育。 (3认同)