Devise authenticate_user! breaks when making remot

2019-07-15 03:53发布

问题:

I am having an issue with Devise' before_action :authenticate_user! filter on remote: true option.

I have a link tag which makes a normal PUT request to messages#read of my MessagesController below

<%= link_to 'Mark Read/Unread', read_message_path(message), method: :put %>

class MessagesController < ApplicationController
  before_action :authenticate_user!, only: :read
  before_action :set_message, only: :read
  ...
  #http PUT
  def read
    @message.toggle!(:read)
    respond_to do |format|
      format.html { redirect_to messages_url, notice: 'Message was successfully destroyed.' }
      format.json { head :no_content }
    end
  end 
  ...
end 

If the user is not signed in, it redirects to the sign in page as the expected Devise behavior.

However when setting the remote: true option on the link e.g.

<%= link_to 'Mark Read/Unread', read_message_path(message), method: :put, remote: true %>

Devise breaks with error: PUT http://localhost:3000/users/sign_in.js 404 (Not Found)

My logs output a 401 Unauthorized followed by

ActionController::RoutingError (No route matches [PUT] "/users/sign_in.js")

The problem is Rails tries to render /users/sign_in.js via PUT which does not exist and shouldn't. This is what trips me off and confuses me becasue I would exptect to be redirected to devise /users/sign_in html page and instead Rails tries to render a sign_in page with .js extension.

I tried setting config/initializers/devise.rb lines to

config.http_authenticatable_on_xhr = false

config.navigational_formats = ['*/*', :html, :js] as suggested here.

I am using Devise with its default settings. No special routes nor controllers.

What am I missing here? am I misunderstading the way Devise is supposed to work?

Is there away to override authenticate_user! to redirect to devise' default sign_in html page on xhr requests?

回答1:

I had a similar problem while doing a jquery ajax call. The strange thing is it was working with a button_to remote call.

My solution was to change the method to POST and expected response type to script:

  $.ajax({
          url: myurl,
          type: 'POST',
          data: {myvar: myval},
          dataType: 'script',


回答2:

Don't present that link if the user isn't logged in.