Rails API ActiveStorage DirectUpload produce 422 E

2020-07-27 16:23发布

问题:

I have a problem with Rails API app active storage. I have React from where i want to upload file.

import React from "react";
import {DirectUpload} from "activestorage";

class SignIn extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      file: null
    };
    this.handleFileChange = this.handleFileChange.bind(this);
    this.handleFileSubmit = this.handleFileSubmit.bind(this);
  }

  handleFileChange(e){
    this.setState({file: e.target.files[0]})
  }

  handleFileSubmit(){
    const upload = new DirectUpload(this.state.file, "/rails/active_storage/direct_uploads");

    upload.create((error, blob) => {
      if(error){
        console.log(error)
      } else {
        console.log(blob)
      }
    })
  }

  render() {

    return (
      <React.Fragment>
          <Form>
          <Form.Item>
            <Input type="file" onChange={this.handleFileChange}/>
          </Form.Item>

          <Form.Item>
            <Button type="primary" htmlType="submit">
              Register
            </Button>
          </Form.Item>
        </Form>
      </React.Fragment>
    );
  }
}

But on submit i got error Completed 422 Unprocessable Entity in 1ms (ActiveRecord: 0.0ms)

Started POST "/rails/active_storage/direct_uploads" for 127.0.0.1 at 2019-05-09 22:59:54 +0200
Processing by ActiveStorage::DirectUploadsController#create as JSON
  Parameters: {"blob"=>{"filename"=>"file.jpg", "content_type"=>"image/jpeg", "byte_size"=>27095, "checksum"=>"8u95dXg39vap1Cq/2fgfbg=="}, "direct_upload"=>{"blob"=>{"filename"=>"file.jpg", "content_type"=>"image/jpeg", "byte_size"=>27095, "checksum"=>"8u95dXg39vap1Cq/2fgfbg=="}}}
Can't verify CSRF token authenticity.
Completed 422 Unprocessable Entity in 1ms (ActiveRecord: 0.0ms)



ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):

actionpack (5.2.3) lib/action_controller/metal/request_forgery_protection.rb:211:in `handle_unverified_request'
actionpack (5.2.3) lib/action_controller/metal/request_forgery_protection.rb:243:in `handle_unverified_request'
devise (4.6.2) lib/devise/controllers/helpers.rb:255:in `handle_unverified_request'
actionpack (5.2.3) lib/action_controller/metal/request_forgery_protection.rb:238:in `verify_authenticity_token'
activesupport (5.2.3) lib/active_support/callbacks.rb:426:in `block in make_lambda'
activesupport (5.2.3) lib/active_support/callbacks.rb:198:in `block (2 levels) in halting'
actionpack (5.2.3) lib/abstract_controller/callbacks.rb:34:in `block (2 levels) in <module:Callbacks>'
activesupport (5.2.3) lib/active_support/callbacks.rb:199:in `block in halting'
activesupport (5.2.3) lib/active_support/callbacks.rb:513:in `block in invoke_before'

I set in application_controller.rb protect_from_forgery with: :null_session but i still got error.

回答1:

I had the same problem. Two ways to solve this :

1. Monkey patch (Not recommended)

in config/initializers/direct_uploads.rb :

require 'active_storage/direct_uploads_controller'

class ActiveStorage::DirectUploadsController
  protect_from_forgery with: :null_session
end

2. Custom controller (recommended)

Let's say you have and API endpoint to /api/v1 :

config/routes.rb

namespace :api do
  scope module: 'v1', path: 'v1' do
    resources :direct_uploads, only: [:create]
  end
end

app/controllers/api/v1/direct_uploads_controller.rb

class Api::V1::DirectUploadsController < ActiveStorage::DirectUploadsController
  # Should only allow null_session in API context, so request is JSON format
  protect_from_forgery with: :null_session, if: Proc.new { |c| c.request.format == 'application/json' }

  # Also, since authenticity verification by cookie is disabled, you should implement you own logic :
  before_action :verify_user

  private

  def verify_user
    raise unless User.find(doorkeeper_token[:resource_owner_id])
  end
end

And change the DirectUpload instanciation with the right endpoint :

const upload = new DirectUpload(this.state.file, "/api/v1/direct_uploads");

Hope this helps. Cheers !