You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails 7生产环境部分ActiveStorage图片无法删除:CSRF令牌验证失败

Rails 7图片删除部分触发CSRF错误问题排查

问题描述

在Rails 7应用中,Product模型与photos为has_many关联,在photos#edit页面展示图片并提供单图删除按钮,大部分图片可正常删除,但部分图片删除时会抛出CSRF验证错误。

相关代码片段

图片删除按钮ERB代码

<% product.photos.each do |photo| %>
  <div class="inline-block">
    <%= image_tag(photo, width: 25) %>
    <%= button_to '<i class="fas fa-times-circle fa-xs" style="margin-left: -10px; position: relative; top: -10px"></i>'.html_safe, 
                  product_photo_path(product, photo), 
                  method: :delete, 
                  class: "btn-invisible",
                  data: { confirm: 'Are you sure?' } 
      %>
    </div>
<% end %>

渲染后的HTML

<img width="25" src="https://lgw-2023.herokuapp.com/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBWWc9IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--6a79941d68ebd75d1a09941f6081eba67e7fd511/Recess%20Kit.jpg" />
<form class="button_to" method="post" action="/products/113/photos/760"><input type="hidden" name="_method" value="delete" autocomplete="off" /><button class="btn-invisible" data-confirm="Are you sure?" type="submit"><i class="fas fa-times-circle fa-xs" style="margin-left: -10px; position: relative; top: -10px"></i></button><input type="hidden" name="authenticity_token" value="K7hCXTJyZPcRi528zAhFtPnfa1bjYg-WphGGb97lnSOh6nNw8ZPFWHhhvUy-CZ1MBYagtxh9Hqs78IsNlTrPaw" autocomplete="off" /></form>

Photos控制器代码

class PhotosController < ApplicationController

  def destroy
    @photo = ActiveStorage::Attachment.find(params[:id])
    product = Product.find(@photo.record_id)
    @photo.purge
    redirect_to edit_product_path(product)
  end

end

Heroku错误日志

2023-11-22T03:42:36.183342+00:00 app[web.1]: I, [2023-11-22T03:42:36.183279 #2]  INFO -- : [bef85162-98ad-485c-b57c-6b36af83a64a] Started DELETE "/products/113" for 70.181.107.14 at 2023-11-22 03:42:36 +0000
2023-11-22T03:42:36.184058+00:00 app[web.1]: I, [2023-11-22T03:42:36.184020 #2]  INFO -- : [bef85162-98ad-485c-b57c-6b36af83a64a] Processing by ProductsController#destroy as HTML
2023-11-22T03:42:36.184099+00:00 app[web.1]: I, [2023-11-22T03:42:36.184072 #2]  INFO -- : [bef85162-98ad-485c-b57c-6b36af83a64a]   Parameters: {"authenticity_token"=>"[FILTERED]", "product"=>{"name"=>"V11 Animal", "product_name_id"=>"14", "sku"=>"", "MSRP"=>"629.99", "lowest_price"=>"629.99", "model"=>"V11", "color"=>"", "dimensions"=>"", "voltage"=>"", "voltage_category"=>"N/A", "photos"=>[""], "historical_models"=>"", "description"=>"", "active"=>"1"}, "id"=>"113"}
2023-11-22T03:42:36.184545+00:00 app[web.1]: W, [2023-11-22T03:42:36.184513 #2]  WARN -- : [bef85162-98ad-485c-b57c-6b36af83a64a] Can't verify CSRF token authenticity.
2023-11-22T03:42:36.184743+00:00 app[web.1]: I, [2023-11-22T03:42:36.184722 #2]  INFO -- : [bef85162-98ad-485c-b57c-6b36af83a64a] Completed 422 Unprocessable Entity in 1ms (ActiveRecord: 0.0ms | Allocations: 340)
2023-11-22T03:42:36.185320+00:00 app[web.1]: F, [2023-11-22T03:42:36.185280 #2] FATAL -- : [bef85162-98ad-485c-b57c-6b36af83a64a]
2023-11-22T03:42:36.185321+00:00 app[web.1]: [bef85162-98ad-485c-b57c-6b36af83a64a] ActionController::InvalidAuthenticityToken (Can't verify CSRF token authenticity.):

原因分析

从错误日志可以明确看到核心问题:

  • 触发CSRF错误的请求是删除产品的DELETE /products/113,而非预期的删除图片的DELETE /products/113/photos/760。
  • 请求参数包含完整的产品表单数据,说明点击图片删除按钮时,实际触发了页面上的产品表单提交(比如产品删除表单或编辑表单)。

导致这个问题的直接原因是:

  • 图片删除按钮使用了margin-left: -10px和position: relative的样式,使得按钮的点击区域超出了自身的<div>容器,覆盖到了页面上的其他表单元素。
  • 用户点击该按钮时,浏览器实际响应的是下层被覆盖的表单提交,而该表单的CSRF token可能已过期,或因表单状态异常导致验证失败。

解决方案

1. 修复按钮定位问题

调整布局,确保图片删除按钮的点击区域完全在自身容器内,不会覆盖其他表单。推荐使用相对定位容器配合绝对定位按钮的方式:

<% product.photos.each do |photo| %>
  <div class="inline-block relative">
    <%= image_tag(photo, width: 25) %>
    <%= button_to '<i class="fas fa-times-circle fa-xs"></i>'.html_safe, 
                  product_photo_path(product, photo), 
                  method: :delete, 
                  class: "btn-invisible absolute -top-2 -left-2",
                  data: { confirm: 'Are you sure?' } 
      %>
  </div>
<% end %>

这里给外层容器添加relative作为定位上下文,按钮用absolute定位到图片左上角,避免点击区域溢出。

2. 验证表单触发目标

使用浏览器开发者工具检查点击按钮时实际触发的表单元素,确认定位问题是否解决。可以给图片删除表单添加独特class,方便排查:

<%= button_to ..., class: "btn-invisible absolute -top-2 -left-2 photo-delete-btn" %>

3. 检查页面其他表单状态

确认edit_product_path页面上的产品删除/编辑表单的CSRF token有效性,若页面加载时间过长,可刷新页面重新获取有效token。


内容的提问来源于stack exchange,提问作者Liz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 18:22:33