ruby on rails - Unable to POST to an endpoint with a file attachment - Stack Overflow

admin2025-04-18  6

I'm working on a Rails API that's supposed to allow for attachments to digital forms using ActiveStorage. My routes are as follows:

  resources :forms, defaults: { format: :json } do
    resources :attachments, except: [ :update ]
  end

My attachment create endpoint is as follows:

  def create
    @form = Form.find(params[:form_id])

    if params[:attachment].present?
      if @form.update(attachments: params[:form][:attachment])
        render json: { message: "Attachment uploaded successfully", file_url: rails_blob_path(@form.attachments.last, only_path: true) }, status: :created
      else
        render json: form.errors, status: :unprocessable_entity
      end
    else
      render json: { error: "No attachment uploaded" }, status: :unprocessable_entity
    end
  end

For testing I created a factory definition:

  FactoryBot.define do
    factory :form do
      user_email { "[email protected]" }
      status { "started" }
    
      trait :with_attachment do
        after (:create) do |form|
          form.attachments.attach(
            io: Rails.root.join("spec", "files", "upload_test_file.pdf").open,
            filename: "upload_test_file.pdf",
            content_type: "application/pdf"
          )
        end
      end
    end
  end

And here's my spec test:

  let(:valid_file) { fixture_file_upload(Rails.root.join("spec", "files", "upload_test_file.pdf"), "application/pdf") }
  let(:myform) { FactoryBot.create(:form) }
  ...
  describe "POST /create" do
    it "creates a new Form Attachment" do
      post "/forms/#{myform.id}/attachments", params: { form: { attachment: valid_file } }, headers: valid_headers, as: :json

      myform.reload
      expect(myform.attachments).to be_attached
      expect(response).to have_http_status(:created)
    end
  end

I suspect it's close but I'm not sure what's causing this error:

  1) /forms/:form_id/attachments POST /create creates a new Form Attachment
     Failure/Error: if @form.update(attachments: params[:form][:attachment])
     
     ArgumentError:
       Could not find or build blob: expected attachable, got #<ActionController::Parameters {"content_type" => "application/pdf", "original_filename" => "upload_test_file.pdf", "tempfile" => "#<File:0x00007f38379ef7c0>"} permitted: false>

All the controller examples I've seen online follow this pattern. I'm also confident I built my model correctly because I'm able to use my Factory pattern to repeatedly build my forms and attachments. And the other request endpoint spec tests run just fine. I suspect there's something trivial I've overlooked.

I'm working on a Rails API that's supposed to allow for attachments to digital forms using ActiveStorage. My routes are as follows:

  resources :forms, defaults: { format: :json } do
    resources :attachments, except: [ :update ]
  end

My attachment create endpoint is as follows:

  def create
    @form = Form.find(params[:form_id])

    if params[:attachment].present?
      if @form.update(attachments: params[:form][:attachment])
        render json: { message: "Attachment uploaded successfully", file_url: rails_blob_path(@form.attachments.last, only_path: true) }, status: :created
      else
        render json: form.errors, status: :unprocessable_entity
      end
    else
      render json: { error: "No attachment uploaded" }, status: :unprocessable_entity
    end
  end

For testing I created a factory definition:

  FactoryBot.define do
    factory :form do
      user_email { "[email protected]" }
      status { "started" }
    
      trait :with_attachment do
        after (:create) do |form|
          form.attachments.attach(
            io: Rails.root.join("spec", "files", "upload_test_file.pdf").open,
            filename: "upload_test_file.pdf",
            content_type: "application/pdf"
          )
        end
      end
    end
  end

And here's my spec test:

  let(:valid_file) { fixture_file_upload(Rails.root.join("spec", "files", "upload_test_file.pdf"), "application/pdf") }
  let(:myform) { FactoryBot.create(:form) }
  ...
  describe "POST /create" do
    it "creates a new Form Attachment" do
      post "/forms/#{myform.id}/attachments", params: { form: { attachment: valid_file } }, headers: valid_headers, as: :json

      myform.reload
      expect(myform.attachments).to be_attached
      expect(response).to have_http_status(:created)
    end
  end

I suspect it's close but I'm not sure what's causing this error:

  1) /forms/:form_id/attachments POST /create creates a new Form Attachment
     Failure/Error: if @form.update(attachments: params[:form][:attachment])
     
     ArgumentError:
       Could not find or build blob: expected attachable, got #<ActionController::Parameters {"content_type" => "application/pdf", "original_filename" => "upload_test_file.pdf", "tempfile" => "#<File:0x00007f38379ef7c0>"} permitted: false>

All the controller examples I've seen online follow this pattern. I'm also confident I built my model correctly because I'm able to use my Factory pattern to repeatedly build my forms and attachments. And the other request endpoint spec tests run just fine. I suspect there's something trivial I've overlooked.

Share Improve this question edited Jan 30 at 7:29 VLAZ 29.2k9 gold badges63 silver badges84 bronze badges asked Jan 30 at 2:10 goterpsgogoterpsgo 3172 silver badges19 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 1

Need to transform the parameter into a blob before attaching it. Something like this…

attachment = params[:form][:attachment]
blob = ActiveStorage::Blob.create_and_upload!(
          io:attachment,
          filename: attachment.original_filename,
          content_type: attachment.content_type,
        )

if @form.update(attachments: blob)

It turns out if you're going to upload a binary file, you're not supposed to include as: :json as part of your command or else everything in your request will turn into text. This is what I ended up using:

post "/forms/#{myform.id}/attachments", params: { attachments: [ file0, file1 ] }, headers: valid_headers
转载请注明原文地址:http://anycun.com/QandA/1744941651a89797.html