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
相关产品推荐
相关产品推荐

