Search code examples
ruby-on-railsdeviseauthorizationpundit

Render and/or redirect called multiple times in action


I'm using Devise and Pundit. To create a new profile page, the user has to be authorized to do so. This has been working fine since I first implemented it, but today it just started acting up with an error message:

Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".

Here's my code from my Application controller:

    rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
    ...

    private

    def user_not_authorized(exception)
        flash[:alert] = "Sorry, you are not authorized to perform this action."
        redirect_to(request.referrer || root_path)
    end

Here's my ProfilePage controller:

def new
  @profile_page = ProfilePage.new
  authorize @profile_page
end

def create
  @profile_page = ProfilePage.new(profile_page_params)
    respond_to do |format|
      if @profile_page.save
        format.html { redirect_to @profile_page, notice: 'Profile page was successfully created.' }
        format.json { render :show, status: :created, location: @profile_page }
      else
        format.html { render :new }
        format.json { render json: @profile_page.errors, status: :unprocessable_entity }
      end
    end
    authorize @profile_page
  end

Someone suggested that I add this line of code below flash[:alert]:

self.response_body = nil

But now my user is redirected to the 'new profile' page again, rather than the successful profile page. It also tells the user that they are not authorized to complete this action, despite the fact that it HAS authorized them to do so.


Solution

  • In the create action you have to put the authorization logic before saving the record:

    You have to move

    authorize @profile_page
    

    at the top of your create action, after initializing the @profile_page, like so:

    def create
      @profile_page = ProfilePage.new(profile_page_params)
        authorize @profile_page
    
        respond_to do |format|
          if @profile_page.save
            format.html { redirect_to @profile_page, notice: 'Profile page was successfully created.' }
            format.json { render :show, status: :created, location: @profile_page }
          else
            format.html { render :new }
            format.json { render json: @profile_page.errors, status: :unprocessable_entity }
          end
        end
    end