使用Rails Stimulus实现资源删除时遇重定向及重复请求问题
问题现象
- 书籍资源已成功删除,但页面无重定向、无删除提示,状态保持不变;
- 浏览器控制台出现
DELETE http://127.0.0.1:3000/books 404 (Not Found)错误,Rails日志显示删除书籍后,又发起了一次指向/books的无效DELETE请求。
原因分析
1. 重复404请求问题
你的button_to参数使用错误:button_to的第二个参数应为目标删除URL,但你将method: :delete直接放在该位置,导致生成的表单action指向当前页面路径(如/books),而非单本书籍的删除路径/books/:id。同时,尽管调用了event.preventDefault(),Turbo的表单提交逻辑仍可能被触发,造成Stimulus发起正确删除请求后,又触发了表单对/books的无效请求,最终出现404。
2. 页面不跳转、无提示问题
你通过fetch发起AJAX删除请求,Rails返回的redirect_to是给浏览器的跳转指令,但AJAX请求不会自动执行跳转,也不会处理flash消息,因此页面无变化、无提示。
修复步骤
1. 修正视图中的button_to代码
调整button_to参数顺序,移除turbo_confirm(避免与SweetAlert2冲突),确保表单指向正确的单本书籍删除URL:
<%= button_to "Delete", url, method: :delete, title: "Remove Book", form: { data: { controller: "sweetalert", action: 'click->sweetalert#confirm', "sweetalert-item-value": item_name, "sweetalert-url-value": url, } }, class: "delete-icon nobutton" do %> <svg>...... <% end %>
注意:此处的
url需为单本书籍的删除路径,例如book_path(book),确保指向/books/:id而非/books。
2. 完善Stimulus控制器,处理响应与跳转
修改Stimulus代码,彻底阻止默认事件,在请求成功后手动处理提示与跳转:
import { Controller } from "@hotwired/stimulus"; import Swal from "sweetalert2"; import { getMetaValue } from "../helpers/index"; import { Turbo } from "@hotwired/turbo-rails"; export default class extends Controller { static values = { item: String, url: String, }; confirm(event) { // 阻止默认行为与事件冒泡,避免表单重复提交 event.preventDefault(); event.stopPropagation(); const csrfToken = getMetaValue("csrf-token"); Swal.fire({ titleText: "Are you sure?", text: `You are about to delete "${this.itemValue}"`, icon: "warning", showCancelButton: true, confirmButtonColor: "#991c00", cancelButtonColor: "#407bbf", confirmButtonText: "Yes, delete it!", }).then(async (result) => { if (result.isConfirmed) { const response = await fetch(this.urlValue, { method: "DELETE", credentials: "same-origin", headers: { "X-CSRF-Token": csrfToken, "Accept": "text/html" }, }); if (response.ok) { // 显示删除成功提示 Swal.fire({ title: "Deleted!", text: `${this.itemValue} has been deleted.`, icon: "success" }); // 用Turbo跳转到书籍列表页 Turbo.visit("/books"); } else { Swal.fire({ title: "Error!", text: "Failed to delete the item.", icon: "error" }); } } }); } }
3. 可选:改用link_to简化实现
若无需button_to的表单提交逻辑,可换成link_to配合data-turbo-method="delete":
<%= link_to title: "Remove Book", url, data: { turbo_method: :delete, controller: "sweetalert", action: 'click->sweetalert#confirm', "sweetalert-item-value": item_name, "sweetalert-url-value": url }, class: "delete-icon nobutton" do %> <svg>...... <% end %>
这种方式下,event.preventDefault()会直接阻止Turbo的默认删除逻辑,确保仅在SweetAlert2确认后发起请求。
额外优化:保留Flash消息
若想保留Rails的flash提示,可修改控制器destroy方法返回Turbo流响应,无需手动跳转:
def destroy @book.destroy respond_to do |format| format.html { redirect_to books_path, alert: "Book removed" } format.turbo_stream do flash.now[:alert] = "Book removed" render turbo_stream: [ turbo_stream.remove(@book), turbo_stream.append("flash", partial: "shared/flash") ] end end end
此时Stimulus请求后,Turbo会自动处理DOM更新与flash消息展示。
内容的提问来源于stack exchange,提问作者tkhobbes

