Reputation: 2694
Due to a previous issue, I have to remove //= require jquery_ujs
from application.js
Now, I need to replace the /users/sign_out
method with an ajax using axios. The following is my code:
axios.delete("users/sign_out", {
headers: {
"X-CSRF-Token": $('meta[name="csrf-token"]').attr('content') },
params: {
"authenticity_token": $('meta[name="csrf-token"]').attr('content')
}
})
.then(function(response) {
alert(response)
})
.catch(function(error) {
alert(error)
})
The server log shows that there is a DELETE "/" right after the delete "/users/sign_out". This is not correct.
Started DELETE "/users/sign_out?authenticity_token=mHQ3d4lJzDNS5TSWEFkDZ%2F3fI0vTDFxW6CabEffaNk6h2JRYNk8kkgCSBOXFdHmgDKcVtY8e29aGU%2F3q9gajWA%3D%3D" for 127.0.0.1 at 2017-08-01 20:59:55 +0800
Processing by Devise::SessionsController#destroy as HTML
Parameters: {"authenticity_token"=>"mHQ3d4lJzDNS5TSWEFkDZ/3fI0vTDFxW6CabEffaNk6h2JRYNk8kkgCSBOXFdHmgDKcVtY8e29aGU/3q9gajWA=="}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [["id", 2], ["LIMIT", 1]]
(0.2ms) BEGIN
(0.2ms) COMMIT
Redirected to http://localhost:3000/
Completed 302 Found in 8ms (ActiveRecord: 0.9ms)
**Started DELETE "/" for 127.0.0.1 at 2017-08-01 20:59:55 +0800**
ActionController::RoutingError (No route matches [DELETE] "/"):
Upvotes: 1
Views: 1073
Reputation: 1
You can also solve this by ensuring the axios
request has an Accept
header with 'application/json'
. As it can be seen in the devise source code for the sessions controller:
def respond_to_on_destroy
# We actually need to hardcode this as Rails default responder doesn't
# support returning empty response on GET request
respond_to do |format|
format.all { head :no_content }
format.any(*navigational_formats) { redirect_to after_sign_out_path_for(resource_name) }
end
end
Which means it should respond with head :no_content
for any non-navigational format, avoiding the redirect.
Upvotes: 0
Reputation: 1124
TL;DR - set config.sign_out_via = :get
in initializers/devise.rb
Devise is responding from the server with a redirect_to
when DELETE /users/sign_out
is requested. The default HTTP status code used by rails for redirect_to
is 302 Found
. The 302
status was originally meant to indicate that the browser should retry the same request with the same method, but this was subverted early on by many browsers which were changing the request method to GET
automatically (see the RFC 1945 Note regarding 302 Moved Temporarily
)
The Rails docs for redirect_to
also have a note about this specifically as it relates to non-standard HTTP request methods via AJAX:
If you are using XHR requests other than GET or POST and redirecting after the request then some browsers will follow the redirect using the original request method. This may lead to undesirable behavior such as a double DELETE.
The solution is to return a 303 See Other
like this: redirect_to resource_path, status: 303
. I have looked for a way to set the HTTP status for the Devise::SessionsController#destroy
and it doesn't seem to exist at this time in the Devise API.
However, you can tell Devise to use GET as the request method for sign out in the Devise initializer:
# in initializers/devise.rb
Devise.setup do |config|
. . .
config.sign_out_via = :get
. . .
end
Now when you visit the sign out link you should see GET /users/sign_out
in your server logs and the browser redirect should also use GET.
Some other resources I used to research this issue:
Upvotes: 6