DoubleRenderError in Controller - ruby-on-rails

before_action :set_blacklisted_nfts, only: %i[blacklisted_nfts]
def blacklisted_nfts
if #blacklisted_nft.present?
redirect_to destroy and return
end
end
def destroy
success = false
message = 'Unable to delete NFT!'
if !current_user.admin?
message = 'This action require admin privileges!'
elsif #nft.destroy
success = true
message = 'NFT deleted successfully!'
end
render layout: false, locals: {
type: success ? 'success' : 'error',
message: message,
nft_id: #nft.id
}
end
private
def set_blacklisted_nfts
#blacklisted_nft = BlacklistedNft.create(contract_address: #nft.contract_address, contract_type: #nft.contract_type, token_id: #nft.token_id, chain: #nft.chain)
end
can anyone guide me where is an issue am having this error:
Error: 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".

Where you have redirect_to destroy that's actually calling the destroy method. You might be trying to redirect to the destroy method, but it's calling it directly.
You can't actually redirect to anything other than another "GET" action, the redirect just responds back to the browser with a URI and the browser just calls a GET on it, you can't have a POST or DELETE or any other http method (aka: http verb).
Probably instead in that condition, you just want to do a #blacklisted_nft.destroy and then redirect_to :back, notice: "Deleted #blacklisted_nft.name" or something.

Related

<AbstractController::DoubleRenderError in controller

I'm getting this error, I tried adding a redirect_to() and return to my access_doc_or_redirect() method, but no luck. Any recommendations?
def access_doc_or_redirect(doc_id, msg)
doc = Document.find(params[:id])
if doc.user_access?(current_user)
#document = doc
else
flash[:alert] = msg
redirect_to root_url and return
end
end
def get
access_doc_or_redirect(params[:id], "Sorry, no document view access.")
redirect_to #document.file.url
end
Error
access_doc_or_redirect(params[:id], "Sorry, no document view access" AbstractController::DoubleRenderError: 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".
AbstractController::DoubleRenderError: Render and/or redirect were called multiple times in this action.
Error is self descriptive you are calling render or redirect multiple times in your action. Lets look at your methods:
def access_doc_or_redirect(doc_id, msg)
doc = Document.find(params[:id])
if doc.user_access?(current_user)
#document = doc
#this block will run fine and return #document to get method
else
flash[:alert] = msg
redirect_to root_url and return
#this block is giving you trouble because you are redirecting to root url and then control goes back to your get method where you are using redirect again and hence double render error
end
end
FIX:
If you are using it as a filter then to fix error you can do:
def get
#i think this is your main method so you should use redirect or render inside this method only
#instance variables set inside access_doc_or_redirect will automatically be available inside this method
if #document
redirect_to #document.file.url
else
flash[:alert] = "Sorry, no document view access."
redirect_to root_url
end
end
def access_doc_or_redirect
#doc = Document.find(params[:id])
if #doc.user_access?(current_user)
#document = doc
end
end

AbstractController::DoubleRenderError Error and using redirect_to

I have an update method which is using save and delete methods having their own render calls. Below is the code:
Update Method:
def update_book
self.delete_book
params[:Name] = params[:NewName]
self.save_book
end
Delete Method:
def delete_book
# Do something
rescue BookDoesNotExist => exception
render status: 404
return
end
head 200
end
Save Method:
def save_book
# Do something
rescue BookDoesNotExist => exception
render status: 404
return
end
rescue BookAlreadyExist => exception
render status: 409
return
end
head 200
end
When I run this with some input the system throws "AbstractController::DoubleRenderError" error along with some 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"
I understand it is because the is throwing exception in both delete and save method with the input I am passing. How do I handle this scenario?
Doing some research I learnt I need to use redirect. So I understand that when my delete method throws exception/renders I should return back and should not call the save method.
Here is what I tried with redirect_to:
Update Method:
def update_book
redirect_to action: self.delete_book and return if status=404 end
params[:Name] = params[:NewName]
self.save_book
end
Am I doing it right or is there any other best way to handle redirect?
When your delete_book or save_book methods receive an exception they will render something and then return, this is correct.
But the return will give execution back to the caller method, which is update_book. This caller method have no idea of the exception that occurred and therefore will do its job and render what it has to render.
To fix, your delete_book or save_book methods need to return a success status to the caller, like this:
def delete_book
# ... Do something
rescue BookDoesNotExist => exception
render status: 404
return false
end
head 200
true
end
Then your caller method shall check for this status:
def update_book
return unless self.delete_book
params[:Name] = params[:NewName]
return unless self.save_book
end
An alternative that will keep your code clear from those heavy exception testing would be to use your controller'srescue_from.
In your ApplicationController:
class ApplicationController < ActionController::Base
rescue_from BookDoesNotExist, with: :redirect_book_not_existing
private
def redirect_book_not_existing
render status: 404
end
end
From now on, very time your controllers receive a BookDoesNotExist exception, the execution will stop and the render status: 404 line will be executed. Off course you must not rescue the exceptions in the other method, like this:
def delete_book
# Do something
head 200
end
Your code will be much cleaner

DoubleRenderError after Net::HTTP.post_form

Hi i want to post xml data to server and redirect page according to response.
For this in my controller i have my action and send_xml method.
Ruby Version: 2.0.0-p247
Rails Version: 3.2.17
def new
#sales = current_user.sales.new
respond_with(#sales)
end
def fail
flash[:error] = 'Canceled'
render :new
end
def success
result = send_xml(params)
if result['Response'] == 'Approved'
flash[:success] = 'Approved'
redirect_to(approved_path)
return
else
flash[:error] = 'Failed'
redirect_to(failed_path)
return
end
end
private
def send_xml(params)
request = "DATA=<?xml version=\"1.0\" encoding=\"ISO-8859-9\"?><myData>Foo</myData>"
uri = URI.parse('http://foobar.com')
xml = render xml: request
response = Net::HTTP.post_form(uri, xml)
response = Hash.from_xml(response.body)
response['myResponse']
end
When i try to run this action i get error like:
AbstractController::DoubleRenderError
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".
I guess NET::HTTP.post_form method triggers render or redirect and that cause this error.
How can i get rid of this error.
Thanks for help
This happens because you are calling render xml: request in #send_xml which you are calling from #success, therefore you are calling render and redirect which results in the exception (as the message also indicates).

AbstractController::DoubleRenderError that shouldn't be

I've been getting the following error when I hit this destroy method in a my User controller.
AbstractController::DoubleRenderError (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".):
It's a strange one, because I honestly am only responding once to the call.
Here's my action:
def destroy
user = User.find(params[:id])
if user.has_role_for? current_client
# then we remove the role
user.has_no_roles_for! current_client
# was that the users only role?
if user.roles.count == 0
user.destroy
end
respond_with head :ok
else
respond_with({:error=>'unauthorised'}, :status => :forbidden)
end
end
Any ideas?
Try adding " and return" after the respond_with lines:
respond_with head :ok and return
respond_with({:error=>'unauthorised'}, :status => :forbidden) and return
head(:ok) doesn't return something you can respond_with. head :ok renders a 200 with no body. respond_with renders via the responder some representation of the object you passed into it. head calls render, respond_with calls render, hence the double render error.
You should change that line to just head :ok.

Render different action for ajax vs html request in rails 3

In rails three I have the following code for my destroy action in a photos controller
def destroy
#photo = Photo.find(params[:id])
if #photo.destroy
flash[:notice] = t('photo.deleted')
respond_to do |format|
if request.xhr?
format.js
else
format.html {redirect_to photos_path}
end
end
else
flash[:alert] = t('.photo.error_deleting')
if request.xhr?
redirect_to(photos_url)
else
redirect_to(photo_path #photo)
end
end
end
The goal is essentially to redirect to the index page if this is called from a standard link and render destroy.js if called from a remote link.
This works but I was wondering if there is a cleaner way of doing this in rails 3. Possibly using the respond_with operator?
Thanks
This should work for you:
respond_to :html, :js
def destroy
#photo = Photo.find(params[:id])
if #photo.destroy
flash[:notice] = t('photo.deleted')
else
flash[:alert] = t('.photo.error_deleting')
end
respond_with(#photo)
end
There is a good blog post about it here:
http://ryandaigle.com/articles/2009/8/10/what-s-new-in-edge-rails-default-restful-rendering
Here's a quote from the post about the logic:
If the :html format was requested:
If it was a GET request, invoke render (which will display the view
template for the current action)
If it was a POST request and the resource has validation errors, render
:new (so the user can fix their
errors)
If it was a PUT request and the resource has validation errors, render
:edit (so the user can fix their
errors)
Else, redirect to the resource location (i.e. user_url)
If another format was requested, (i.e. :xml or :json)
If it was a GET request, invoke the :to_format method on the resource and
send that back
If the resource has validation errors, send back the errors in the
requested format with the
:unprocessable_entity status code
If it was a POST request, invoke the :to_format method on the resource and
send that back with the :created
status and the :location of the new
created resource
Else, send back the :ok response with no body
A little more on the to_format part from the documentation:
First we try to render a template, if
the template is not available, we
verify if the resource responds to
:to_format and display it.
There is also a Railscast about it:
http://railscasts.com/episodes/224-controllers-in-rails-3

Resources