Reputation: 313
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.
Upvotes: 3
Views: 3314
Reputation: 2344
Rails 7 (but could have been introduced before) the documentation accessible here mentions the following:
// your form needs the file_field direct_upload: true, which
// provides data-direct-upload-url, data-direct-upload-token
// and data-direct-upload-attachment-name
Here is the inside of one of my stimulus controller as an example (should work the same with minor adaptions with React):
addFile(event) {
const files = event.target.files;
Array.from(files).forEach((file) => {
console.log(file);
const url = event.target.dataset.directUploadUrl;
const token = event.target.dataset.directUploadToken;
const attachmentName = event.target.dataset.directUploadAttachmentName;
console.log(`attachmentName: ${attachmentName}`);
const upload = new DirectUpload(file, url, token, attachmentName);
upload.create((error, blob) => {
if (error) {
console.error(error);
return;
} else {
// DO SOME STUFF WITH THE UPLOADED IMAGE
}
});
});
}
Upvotes: 1
Reputation: 2742
Use a custom controller with skip_forgery_protection
to disable forgery protection on API only mode.
# app/controllers/api/v1/direct_uploads_controller.rb
class Api::V1::DirectUploadsController < ActiveStorage::DirectUploadsController
skip_forgery_protection
end
Upvotes: 0
Reputation: 913
I had the same problem. Two ways to solve this :
in config/initializers/direct_uploads.rb
:
require 'active_storage/direct_uploads_controller'
class ActiveStorage::DirectUploadsController
protect_from_forgery with: :null_session
end
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 !
Upvotes: 6