Making Devise respond to both html and json?

2019-05-29 10:31发布

问题:

I am working on an app which needs has both website and an api and I am implementing a common Devise authentication for that. To implement this requirement I am overriding Devise Registrations Controller to respond to both html and json. For json I want the controller to return the user data and I am able to implement this, but for html I want to use the original devise behavior of signing in and redirecting to root path.

The code for Registrations Controller is:

class RegistrationsController < Devise::RegistrationsController

 def create
  @user = User.create(user_params)
  respond_to do |format|
    format.html {
      @user.save ? (super) : (render :new)
    }
    format.json {
      @user.save ? (render :json => {:state => {:code => 0}, :data => @user }) : 
                   (render :json => {:state => {:code => 1, :messages => @user.errors.full_messages} })
    }
  end
end

private

  def user_params
     params.require(:user).permit(:email, :password)
  end
end 

Using this controller I get a validation error Email has already been taken but when I check my logs it shows the user has been created.

Can anyone tell me what error I am doing? I just want my controller to fallback to original devise functionality in case of html request.

回答1:

The problem was when you used HTML, you created the user twice by running both @user = User.create(user_params) and super. I moved that first line of code to JSON format, and HTML to only run super.

class RegistrationsController < Devise::RegistrationsController
  def create

    respond_to do |format|
      format.html {
        super
      }
      format.json {
        @user = User.create(user_params)
        @user.save ? (render :json => {:state => {:code => 0}, :data => @user }) : 
                     (render :json => {:state => {:code => 1, :messages => @user.errors.full_messages} })
      }
    end
  end

private

    def user_params
      params.require(:user).permit(:email, :password)
    end
  end 


回答2:

Here is the working version of the code (tested locally myself). You controller should look like:

def create
  @user = User.create(user_params)
  respond_to do |format|
    format.html {
      @user.save ? (render @user) : (render 'layouts/application')
    }

    format.json {
      @user.save ? (render :json => {:state => {:code => 0}, :data => @user }) :
                   (render :json => {:state => {:code => 1, :messages => @user.errors.full_messages} })
    }
  end
end

Add a _user partial app/views/users/_user.html.erb:

<p>
  <%= @user.email %>
  <%= @user.created_at %>
  <%= @user.updated_at %>
</p>

So, the changes are minimal. Once you make these changes, your RegistrationsController#create action will work successfully for HTML format.