So I'm trying to create a Rails API with devise. My client side works well with GET, but I can't do POST type of calls. If I try to create a user I keep getting the following devise html form as response.
<h2>Sign up</h2>
<form class="new_user" id="new_user" action="/users" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓" /><input type="hidden" name="authenticity_token" value="RGZxLuXcOCbxKVdpFc/wSAKEp3wcxqpfnvmv0nstEcOppPyViWcmdu+dPt84XQeSkCmwRg0REZN+vTdX1j+MOQ==" />
<div id="error_explanation">
<h2>2 errors prohibited this user from being saved:</h2>
<ul><li>Email can't be blank</li><li>Password can't be blank</li></ul>
</div>
<div class="field">
<div class="field_with_errors"><label for="user_email">Email</label></div><br />
<div class="field_with_errors"><input autofocus="autofocus" type="email" name="user[email]" id="user_email" /></div>
</div>
<div class="field">
<div class="field_with_errors"><label for="user_password">Password</label></div>
<em>(4 characters minimum)</em>
<br />
<div class="field_with_errors"><input autocomplete="off" type="password" name="user[password]" id="user_password" /></div>
</div>
<div class="field">
<label for="user_password_confirmation">Password confirmation</label><br />
<input autocomplete="off" type="password" name="user[password_confirmation]" id="user_password_confirmation" />
</div>
<div class="actions">
<input type="submit" name="commit" value="Sign up" />
</div>
</form>
<a href="/users/sign_in">Log in</a><br />
<a href="/users/confirmation/new">Didn't receive confirmation instructions?</a><br />
Interestingly, at first I would get a "Can't verify CSRF token authenticity" error, so I added beforeSend to my JSON call. This would than change my call to OPTIONS, even though it was specified as a POST.
My call:
$(document).on('page:change', function(){
$('body').on('submit', 'form.create_new_user', function(e){
e.preventDefault();
var user_data = $(this).serializeJSON();
$.ajax({
url: Host.address + '/users',
beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},
type: 'post',
datatype:'json',
data: {info: {full_name: user_data.full_name,
email: user_data.email,
password: user_data.password,
password_confirmation: user_data.password_confirmation}}
}).done(function(response){
console.log(response)
if (response.success.success) {
logInGoToWithNotice(response, '/', 'Thank you for signig up');
} else{
alert("User wasn't created. Please try again.")
};
});
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
And API does this:
Started OPTIONS "/users" for ::1 at 2015-09-30 14:09:49 +0200
ActionController::RoutingError (uninitialized constant RegistrationsController):
...
If I now remove beforeSend, I'd get the form again, just that "Can't verify CSRF token authenticity" error wouldn't be there anymore, but instead I'd get successful POST, that still returns html form.
Started POST "/users" for ::1 at 2015-09-30 14:25:57 +0200
Processing by Devise::RegistrationsController#create as */*
Parameters: {"info"=>{"full_name"=>"User Admin", "email"=>"user.admin@email.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}}
(0.1ms) BEGIN
(0.1ms) ROLLBACK
Rendered /Users/antonpot/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/devise-3.5.2/app/views/devise/shared/_links.html.erb (0.2ms)
Rendered /Users/antonpot/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/devise-3.5.2/app/views/devise/registrations/new.html.erb (3.5ms)
Completed 200 OK in 9ms (Views: 4.7ms | ActiveRecord: 0.2ms)
Now if I look into my Users Controller I can see I'm never even running my #create, as it never prints p "C"*99
class Users::RegistrationsController < Devise::RegistrationsController
def create
p "C"*99
user = User.new
user.full_name = user_params[:full_name]
user.email = user_params[:email].downcase
user.password = user_params[:password]
user.password_confirmation = user_params[:password_confirmation]
if user.save
render json: {success:{success: true, user_id: user.id, errorMessage: nil, errorNumber: 201}}
else
render json: {success:{success: false, errorMessage: user.errors.messages.to_s, errorNumber: 400}}
end
end
end
Why can't I call it? What do I need to do for this to work?
First
This issue because of cross-site-scripting problem. The client url
is different than the api url
. The HTTP method OPTIONS
, is a pre-flight request that the browser makes to determine whether a cross-domain AJAX
request should be allowed.
if cross-domain AJAX
request is allowed the POST
which is the actual request will be sent.
So you need to allow cross-domain AJAX
request on your server.
Second
Make sure your routes.rb
is set correctly.
You need to configure your devise and instruct him to use your customized registration_controller
instead of default devise registration controller
adjust devise_for :users
in your routes.rb
to be something like the following:
devise_for :users, controllers: { registrations: "users/registrations"}
assuming that the customized registration class
exist in the users
folder