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

Rails不同模型配置多存储桶后Stimulus JS直传报422错误

Rails Stimulus直传多存储桶配置解决方案

问题原因

后端通过service: :avatar指定了存储服务,但Stimulus的DirectUpload默认不会把这个服务名参数传给后端的/rails/active_storage/direct_uploads端点,导致后端验证时触发Service name can't be blank的422错误。

解决步骤

1. 给DirectUpload请求追加服务名参数

修改你的Stimulus控制器,在初始化DirectUpload实例时,通过请求头或FormData传递服务名:

// app/javascript/controllers/direct_uploads_controller.js
import { Controller } from "@hotwired/stimulus"
import { DirectUpload } from "@rails/activestorage"

export default class extends Controller {
  connect() {
    this.input.addEventListener("change", this.handleFileAdded.bind(this))
  }

  handleFileAdded(event) {
    const files = Array.from(event.target.files)
    const service = this.element.dataset.service // 从元素属性取服务名

    files.forEach(file => {
      const upload = new DirectUpload(file, this.directUploadUrl, {
        directUploadWillCreateBlobWithXHR: (xhr) => {
          // 通过请求头传递服务名
          xhr.setRequestHeader('X-Service-Name', service)
        }
      })

      upload.create((error, blob) => {
        if (error) {
          console.error(error)
        } else {
          // 将Blob的signed_id插入表单隐藏字段
          const hiddenField = document.createElement('input')
          hiddenField.type = 'hidden'
          hiddenField.name = this.input.dataset.fieldName
          hiddenField.value = blob.signed_id
          this.element.appendChild(hiddenField)
        }
      })
    })
  }

  get directUploadUrl() {
    return this.element.dataset.directUploadUrl || "/rails/active_storage/direct_uploads"
  }

  get input() {
    return this.element.querySelector("input[type=file]")
  }
}

2. 自定义后端直传控制器接收服务名

默认ActiveStorage的DirectUploads控制器不会处理服务名参数,需要自定义控制器来传递参数给Blob创建:

# app/controllers/custom_direct_uploads_controller.rb
class CustomDirectUploadsController < ActiveStorage::DirectUploadsController
  protected
    def create_blob_before_direct_upload!(blob_args)
      # 从请求头或参数中获取服务名
      service_name = request.headers['X-Service-Name'] || params[:service]
      super(blob_args.merge(service: service_name))
    end
end

然后替换路由:

# config/routes.rb
Rails.application.routes.draw do
  # 替换默认直传路由
  post "/rails/active_storage/direct_uploads", to: "custom_direct_uploads#create"
  # 其他路由...
end

3. 视图层传递服务名标识

在表单的文件上传容器上添加data-service属性,指定对应模型的存储服务名:

<%= form_with model: @user do |form| %>
  <div data-controller="direct-uploads" data-direct-uploads-service="avatar">
    <%= form.file_field :avatar, data: { field_name: "user[avatar]" } %>
  </div>
  <%= form.submit %>
<% end %>

验证效果

完成以上配置后,Stimulus直传时会把指定的服务名传递给后端,后端创建Blob时会使用对应存储桶的服务,即可解决422验证错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 23:11:09