ChatGPT解决这个技术问题 Extra ChatGPT

No route matches "/users/sign_out" devise rails 3

I've installed devise on my app and applied the following in my application.html.erb file:

<div id="user_nav">
    <% if user_signed_in? %>
        Signed in as <%= current_user.email %>. This cannot be cheese?
        <%= link_to 'Sign out', destroy_user_session_path %>
    <% else %>
        <%= link_to 'Register', new_user_registration_path %> or <%= link_to 'Sign in', new_user_session_path %>
    <% end %>
</div>

I ran rake routes and confirmed that all the routes are valid.

Also, in my routes.rb file I have devise_for :users and root :to => "home#index".

I get the following routing error when clicking the "Sign out" link:

No route matches "/users/sign_out"

Any ideas what's causing the error?

Did you restart your app after adding the routes? Route changes only become effective on startup.
Yes. Just did it again to be safe. Also, I read somewhere else on Stack that it could be an issue with the newest devise gem not being compatible with Rails 3.0.3 so I tried changing my devise gem from 1.4.2 to gem 'devise', :git => 'git://github.com/plataformatec/devise.git'. That did nothing though.
Wouldn't changing the entry in the Gemfile just get you an ever newer version of Devise? Have you tried specifying a lower version number?
FWIW, this can ALSO happen (fails to signout) if your browser is blocking javascript, which I guess blocks DELETE actions. I ran into this today after starting to use the NoScript add-on for firefox.
I only started seeing this after moving to a custom domain. The DELETE approach was working on myapp-staging.herokuapp.com, but broke once I moved to myapp.com. Using GET works, but makes me feel a bit dirty.

K
K M Rakibul Islam

I think the route for signing out is a DELETE method. This means that your sign out link needs to look like this:

<%= link_to "Sign out", destroy_user_session_path, :method => :delete %>

Yours doesn't include the :method => :delete part. Also, please note that for this to work you must also include <%= javascript_include_tag :defaults %> in your layout file (application.html.erb).


I can safely say I've never had to do that in any of my Rails apps. link_to "Sign out", destroy_user_session_path has always been good enough for me.
I did the test and it works for me. Don't forget that things change from one version to another (be it in Rails or Devise). Besides, logging out is state-changing behaviour which should not be done using GET methods (in my humble opinion).
For more information, the rationale why this was changed in the latest version of Devise is described here and here.
I ran into this same issue and Jessie's suggestion worked for me. I expect anyone who is running through the devise railscast to eventually end up here due to this change...
you can also type this into your web console to test it out $("<a href='/users/sign_out' data-method='delete'>Sign out</a>").appendTo('body'); - if jQuery is loaded on the page.
k
kitdesai

I changed this line in devise.rb:

config.sign_out_via = :delete

to

config.sign_out_via = :get

and it started working for me.


The way it was done before was to sign out using "GET /users/sign_out", but they changed it to "DELETE" to make it more RESTful. The author explained that a GET shouldn't make changes to the server such as logging out.
this worked for me. Whilst I appreciate it's not a best practise, the other answers failed. I can't see why!!
It is also possible to use an array if you wish to support multiple methods. For example: config.sign_out_via = [ :post, :delete ] or devise_for :users, :sign_out_via => [ :post, :delete ], as described in devise/rails/routes.rb.
I wouldn't use a GET request for signing out as it opens the user up to a XSRF attack. (Hacker creates a website with an image tag with src="foo.com/users/sign_out", user visits it and is logged out).
@RyanDoherty Id I'm not mistaken, XSRF is still possible (the attacker just has to use a form with action="DELETE" on the target url, then auto-submit it at the page load).
D
Drew Stephens

You probably didn't include jquery_ujs javascript file. Make sure you are using the latest version of jquery-ujs : https://github.com/rails/jquery-ujs and the last files available :

rails generate jquery:install

You should not have any more rails.js file. If you do, you're probably out-of-date. Make sure also this file is loaded with defaults, in config/application.rb

config.action_view.javascript_expansions[:defaults] = %w(jquery.min jquery_ujs)

(Again, you should not have rails.js file here). Finally, add the link as documented on Devise wiki (haml-style):

= link_to('Logout', destroy_user_session_path, :method => 'delete')

And everything will be fine.


I had :method => 'delete' in my link_to, the problem was jquery_ujs not included, this solution solved my problem. Remember to put gem 'jquery-rails' in your gemfile.
Thanks this worked for me. Instead of using defaults you can also use javascript_include_tag "jquery_ujs"
Good catch, thank you! I am using require.js to load files asynchronously and forgot to require jquery_ujs.
I assumed the "u" meant uncompressed and removed this line from application.js. Bad assumption, I suppose. Thanks.
This problem cropped up (in my 100th devise based app) after changing out twitter bootstrap from less to sass and I forgot to add back the //= require jquery_ujs into my application.js.
W
Will Nathan

The ability to make the Logout link a DELETE RESTful call requires an html attribute data-method = "delete" by using the rails code = link_to('Logout', destroy_user_session_path, :method => :delete).

However, if you do not have the gem jquery-ujs installed or are not calling the resulting javascript in your application.html via = javascript_include_tag "application", the response will be sent as a GET request, and the route will fail.

You have a few options if you do not want to use jquery-ujs or cannot find a way to make it work:

Change config.sign_out_via to equal :get within devise.rb (not recommended, since DELETE is the appropriate RESTful query) OR Change the link_to to = button_to('Logout', destroy_user_session_path, :method => :delete). With button_to Rails will do the heavy lifting on making the proper DELETE call. You can then style the button to look like a link if you wish.


The change from link_to to button_to worked for me, but i really don't understand why? What exactly changed apart from the html/css?
It has to do with the rails 'magic' that auto generates html from functions like button_to and link_to. It just so happens that the rails magic for button_to specifies the proper RESTful DELETE call and link_to does not. If I had to guess, I'd say the reason is that html button elements can handle a DELETE call (or bundle with a hidden field specifying the action) while regular links cannot.
Thanks for this. I setup my app to manually handle jquery, so I didn't have jquery-ujs. Installing with bower or bundler and including the appropriate line for the asset pipeline fixes the issue.
About button_to, Rails generates a form for the whole action. That's why it's possible to use :delete method with button_to and not with link_to, just see the generated HTML.
D
Dave Newton

Try adding a new route to devise/sessions#destroy and linking to that. Eg:

routes.rb
devise_for :users do
  get 'logout' => 'devise/sessions#destroy'
end

view:

<%= link_to "Logout", logout_path %>

I also had the same error as mmichael. The solution above will work but it's not how it's supposed to be fixed. The default routes in Devise already include the signing out route as a DELETE method. You should normally not need to change the default routes themselves. That's why you can fix it by simply adding a separate parameter to the link_to call, as described in the other answer.
You don't have to add a route to the route.rb fiile, devise lets you change the method in devise.rb which is in the /confit/initializers/ directory.
Anyone else can confirm what Jagira says. Also i find this answer as properly, i am implementing this on all my projects
How do you set a path if it was routes.rb get '/:lang/user/logout' => 'devise/session#destroy' view <%= link_to "Logout", ??? %> ? Thanks
The syntax is now: devise_for :users then the gets must be put in devise_scope :user do NOTE: singular :user for devise_scope. see the answer from dipole_moment.
p
pjumble

Use it in your routes.rb file:

devise_for :users do
    get '/users/sign_out' => 'devise/sessions#destroy'
end

g
gringo

I had the same problem with rails 3.1.0, and I solved adding in file the followings lines:

app/assets/javascripts/application.js
//= require_tree
//= require jquery
//= require jquery_ujs

If using bower try //=require jquery-ujs.
J
Jon

With one exception, Jessie's answer worked for me:

<%= link_to "Sign out", destroy_user_session_path, :method => :delete %>

change:

:delete

... to:

'delete'

So the code that worked for me is:

<%= link_to "Sign out", destroy_user_session_path, :method => 'delete' %>

L
Lukasz Muzyka

Many answers to the question already. For me the problem was two fold:

when I expand my routes: devise_for :users do get '/users/sign_out' => 'devise/sessions#destroy' end I was getting warning that this is depreciated so I have replaced it with: devise_scope :users do get '/users/sign_out' => 'devise/sessions#destroy' end I thought I will remove my jQuery. Bad choice. Devise is using jQuery to "fake" DELETE request and send it as GET. Therefore you need to: //= require jquery //= require jquery_ujs and of course same link as many mentioned before: <%= link_to "Sign out", destroy_user_session_path, :method => :delete %>


Certainly. Your website is not really sending any DELETE requests. Quite frankly it will only use GET and POST. Because in Rails community it has been agreed that (for good reasons) we want to delete records when submitting DELETE request we need to use a little trick. When in your ERB you specify method: :delete rails will convert that to HTML5 tag: data-method="delete" and submit it as GET. Now, this is where jQuery-ujs and jQuery kick in. They allow app to recognise this is what you're doing. So that your request can be matched with controller action.
without those gems, this will not work. Of course, you can change settings of devise (in devise initialiser) and ask it to use GET instead of DELETE.
A
Amal Kumar S

Add:

  <%= csrf_meta_tag %>  and 
  <%= javascript_include_tag :defaults %>  to layouts

Use these link_to tags

 link_to 'Sign out', destroy_user_session_path, :method => :delete

  or

 link_to 'Sign out', '/users/sign_out', :method => :delete

In routes add:

  devise_for :users do
    get '/users/sign_out' => 'devise/sessions#destroy'
  end

R
Rodrigo Flores

Other option is to configure the logout to be a GET instead a DELETE, you can do that adding the following line on /config/initializers/devise.rb

config.sign_out_via = :get

But as Steve Klabnik wrote on his blog (http://blog.steveklabnik.com/2011/12/11/devise-actioncontroller-routingerror-no-route-matches-get-slash-users-slash-sign-out.html) try to use DELETE because of the semantic of this method.


S
StlTenny

If you are using Rails 3.1 make sure your application.html.erb sign out looks like:

<%= link_to "Sign out", destroy_user_session_path, :method => :delete %>

And that your javascript include line looks like the following

<%= javascript_include_tag 'application' %>

My guess is that some gems overwrite the new structure of the default.js location.


S
Shane

Check it out with source code in github:

https://github.com/plataformatec/devise/commit/adb127bb3e3b334cba903db2c21710e8c41c2b40#lib/generators/templates/devise.rb (date : June 27, 2011 )

# The default HTTP method used to sign out a resource. Default is :get. 188

# config.sign_out_via = :get 187

# The default HTTP method used to sign out a resource. Default is :delete. 188

config.sign_out_via = :delete


w
workdreamer

Well, guys for me it was only remove the :method => :delete

<%= link_to('Sign out', destroy_user_session_path) %>

D
Deepak Lamichhane

This means you haven't generated the jquery files after you have installed the jquery-rails gem. So first you need to generate it.

rails generate devise:install

First Option:

This means either you have to change the following line on /config/initializers/devise.rb

config.sign_out_via = :delete to config.sign_out_via = :get

Second Option:

You only change this line <%= link_to "Sign out", destroy_user_session_path %> to <%= link_to "Sign out", destroy_user_session_path, :method => :delete %> on the view file.

Usually :method => :delete is not written by default.


N
NoNaMe

I want to add to this even though it's a bit old.

the "sign_out" link didn't work, despite having :method => :delete.

The comment indicating that <%= javascript_include_tag :defaults %> must be included reminded me I had recently added JQuery java script and used simple <script src=""/> tags to include them.

When I moved them from after the :defaults to before, the sign_out started working again.

Hopefully this helps someone.


J
Jerome

Most answers are partial. I have hit this issue many times. Two things need to be addressed:

<%= link_to(t('logout'), destroy_user_session_path, :method => :delete) %>

the delete method needs to be specified

Then devise uses jquery, so you need to load those

   <%= javascript_include_tag "myDirectiveJSfile" %> 

and ensure that BOTH jquery and jquery-ujs are specified in your myDirectiveJSfile.js

//= require jquery
//= require jquery_ujs

p
peter.hrasko.sk

Don't forget to include the following line in your application.js (Rails 3)

//= require_self
//= require jquery
//= require jquery_ujs

Include jquery_ujs into my rails application and it works now.


T
Tyler Collier

If you're using HTTPS with devise, it'll break if your sign-out link is to the non-secure version. On the back end, it redirects to the secure version. That redirect is a GET, which causes the issue.

Make sure your link uses HTTPS. You can force it with protocol: "https" in your url helper (make sure you use the url helper and not the path helper).

<%= link_to "Sign out", destroy_user_session_url(protocol: "https"), method: :delete %>

d
dipole_moment
  devise_for :users
  devise_scope :user do
    get '/users/sign_out' => 'devise/sessions#destroy'
  end

S
Stephan

The problem begin with rails 3.1... in /app/assets/javascript/ just look for application.js.

If the file doesn't exist create a file with that name I don't know why my file disappear or never was created on "rails new app"....

That file is the instance for jquery....


j
jon snow

Lots of solutions are there. but mostly use this,

<%= link_to 'Sign out', destroy_user_session_path, method: :delete %>

or config devise.rb with proper sign_out method

In devise.rb

config.sign_out_via = :delete ( or  :get which u like to use.) 

Z
Zakaria

use :get and :delete method for your path:

devise_scope :user do
  match '/users/sign_out' => 'devise/sessions#destroy', :as => :destroy_user_session, via: [:get, :delete]
end

Note: there must be the devise_for :users declared separately from this. see the answer from dipole_moment.
U
Uladz Kha

In your routes.rb :

 devise_for :users do
    get '/sign_out' => 'devise/sessions#destroy'
    get '/log_in' => 'devise/sessions#new'
    get '/log_out' => 'devise/sessions#destroy'
    get '/sign_up' => 'devise/registrations#new'
    get '/edit_profile' => 'devise/registrations#edit'
 end

and in your application.html.erb:

<%if user_signed_in?%>
          <li><%= link_to "Sign_out", sign_out_path %></li>
<% end %>

The syntax is now: devise_for :users then the gets must be put in devise_scope :user do NOTE: singular :user for devise_scope
P
Peter O.

This is what I did (with Rails 3.0 and Devise 1.4.2):

Make sure your page loads rails.js Use this param: 'data-method' => 'delete' Good idea to add this param: :rel => 'nofollow'


C
Charles Magid

See if your routes.rb has a "resource :users" before a "devise_for :users" then try swapping them:

Works devise_for :users resources :users Fails resources :users devise_for :users


C
Code Lღver

the ':method => :delete' in page is 'data-method="delete"' so your page must have jquery_ujs.js, it will submit link with method delete not method get


m
mikeym

I know this is an old question based on Rails 3 but I just ran into and solved it on Rails 4.0.4. So thought I'd pitch in how I fixed it for anyone encountering this problem with this version. Your mileage may vary but here's what worked for me.

First make sure you have the gems installed and run bundle install.

gem 'jquery-rails'

gem 'turbolinks'

gem 'jquery-turbolinks'

In application.js check that everything is required like below.

Beware if this gotcha: it's //= require jquery.turbolinks and not //= require jquery-turbolinks

//= require jquery
//= require jquery_ujs
//= require jquery.turbolinks
//= require turbolinks
//= require_tree .

Next, add the appropriate links in the header of application.html.erb.

<%= javascript_include_tag  "application", "data-turbolinks-track" => true %>
<%= javascript_include_tag :defaults %>

There seems to be many variations on how to implement the delete method which I assume depends on the version of Rails you are using. This is the delete syntax I used.

<p><%= link_to "Sign Out", destroy_user_session_path, :method => 'delete' %></p>

Hope that helps dig someone out of this very frustrating hole!


M
Muntasim

In general when you get "No route matches" but you think you have that route defined then double check the http verb / request method (whether its get, put, post, delete etc.) for that route.

If you run rake routes then you will see the expected method and you can compare this with the request log.


a
antonkronaj

I am using rails 7. So This was how I had to do it. The important bit is data: { turbo_method: :delete }

<%= link_to t('nav.logout'), destroy_user_session_path, class: "nav-link", data: { turbo_method: :delete } %>

Below were the defaults created by rails when I generated the project.

application.html.erb

<%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>

application.js

import "@hotwired/turbo-rails"
import "./controllers"
import * as bootstrap from "bootstrap"