My rendering in Rails (possibly) causes a 422 error - ruby-on-rails

I have received reports from users to my website that they get Error 422 when visiting a "result" page using POST. I cannot re-create this error at all so I am wondering if there is anything in my code below that would cause this error in formatting? I expect there could be errors here since I have upgraded a Rails 3.x project to a Rails 4.2.
I would either like to know if there is anything obvious in the code that would create 422 errors or if there is anyway to troubleshoot 422-errors.
Basically, in #show there is a POST method to result. It is creating a result text and lands on a url like /this-post-name/result?r=abc123 . I am rendering #show in /result because it is basically loading the same page again but with a "result box". Having to use /result is a choice I made as a newbie programmer and is not absolutely necessary, I think.
I am quite sure the error lies within the "respond_to" but can't figure that out, or troubleshoot it (i.e. re-create it).
Also, I am not sure if this is important, but I get tons of AuthencityToken errors on this page.
Edit: I managed to recreate this issue by accessing it through my iPhone and post a form, then I disabled cookies and send the form again. That would not be something people would do often but I guess having cookies disabled may cause this?
def show
#avaliable_posts = Post.where(:available => true)
end
def result
if request.get? && params[:r].blank? # Just visiting /result withoutout POST or ?r url
redirect_to category_path(#category)
else
set_round(session[:round_by_number])
# Either the visitor just posted the result or is revisiting through URL
if !params[:r].blank? # Visitor arrived from URL
#result = Result.find_by_scrambled_identifier(params[:r])
params_to_use = #result.params_used
#params_to_use = #result.params_used
else
params_to_use = params
#params_to_use = params_to_use
end
post_instance = #post.get_post_instance(params_to_use)
if post_instance.valid?
#post_result_array = post_instance.calculate_me(params_to_use)
#post_result_text_array = #post_result_array[0]
respond_to do |format|
format.html { render :action => "show" }
format.json { render :json => #post }
end
else # post not valid
#errors = post_instance.errors
respond_to do |format|
format.html { render :action => "show" }
format.xml { render :xml => #validator.errors, :status => :unprocessable_entity }
format.json { render :json => #post }
end
end
end
end

A 422 means Unprocessable Entity. Within your sample code is only one place with this http status code:
format.xml { render :xml => #validator.errors, :status => :unprocessable_entity }
Obviously this happens when format is XML and #validator contains an error.
Edit:
With the new information about the exception within the logs and the second linked stackoverflow question it seems to be releated to a known Rails issue

It seems like this issue is related to another issue that I have written another question for. I have an InvalidAuthencityToken issue with my website and the exceptions created through that cause a 422 (and not a 500) error as far as I understand from http://api.rubyonrails.org/v2.3/classes/ActionController/RequestForgeryProtection/ClassMethods.html
I am not 100% sure that this is the same issue but it seems quite likely and therefore I will close this question.

Related

Rails navigates back to error page after long running process

This is very likely to be the wrong way of doing it but I hope someone can point me in the correct direction
The problem is that some_process takes a while to execute if there is an error and if the browser goes to another page in the meanwhile, the page comes back to the edit page when it (some_process) does complete eventually.
(greatly simplified code)
def update
respond_to do |format|
result = some_process
if result
format.html { redirect_to :action => :index }
else
# error
format.html { render :edit }
end
end
end
How do you prevent this? some_process cannot be interrupted. Should it be run in another thread? Is there some method to prevent rendering the page if the user has lost interest and already gone elsewhere?
Should I check something before render :edit to see whether it is still on the same page?
Total Rails newbie question & I've exhausted my Google-fu.

Rails redirect if validation fails

In a Rails 3.2 app, I have a validation for an attachment type.
Attachment model:
class Attachment < ActiveRecord::Base
validates_presence_of :name
validates_attachment_presence :attach, :message => "No file selected"
validate :check_type
def check_type
if self.costproject_id != nil
if self.attach_content_type != 'application/pdf'
self.errors.add(:pdf, " ONLY")
return false
end
end
end
But, the return false sends me to this URL:
http://localhost:3000/attachments
I want it to go back to the previous input screen:
http://localhost:3000/attachments/new?costproject_id=2
How do I accomplish that?
Thanks!!
UPDATE1
Perhaps the redirect has to take place in the controller?
format.html { render action: "new" }
Attachment controller:
# POST /attachments
# POST /attachments.json
def create
#attachment = Attachment.new(params[:attachment])
respond_to do |format|
if #attachment.save
format.html { redirect_to session.delete(:return_to), notice: 'Attachment was successfully created.' }
format.json { render json: #attachment, status: :created, location: #attachment }
else
format.html { render action: "new" }
format.json { render json: #attachment.errors, status: :unprocessable_entity }
end
end
end
I changed this line:
format.html { render action: "new" }
To:
format.html { redirect_to request.referer }
And now it goes back to where I want. But, I've lost the errors - they don't display.
To help you understand what's going on here. When you go to /attachments/new you are rendering a form. When you press submit, you are sending a POST request to /attachments, which invokes the create action.
You're create action appears to be solid and idomatic. However when you render action: "new" in the case of an error, it's not a full redirect, it's rendering the form in the context of the current action.
Normally this is fine, because idomatic rails would have you building a single, very similar, model object in both new and create, and the form for helper would render that object. However your new action is creating all kinds of objects based on a large assortment of query parameters, which I'm guessing is why you are seeing behavior you don't like.
I expect your final solution will involve bringing all those parameters into Attachment in some way, if they don't need to be saved to the database, you can make attr_accessors on Attachment
# Model
class Attachment < ActiveRecord::Base
attr_accessor :worequest_id, :workorder_id # etc
end
# View
<%= form_for #attachment do |f| %>
<%= f.hidden :worequest_id %>
<% end %>
Approaching it this way, your post request params will look like
{
attachment:
{
worequest_id: 1,
# etc
}
}
And you would also need to rework your query params to nest the inidividual ids inside of an attachment
/attachments/new?[attachment][worequest_id]=1
This way you could build attachment from params in both actions:
Attachment.new(params[:attachment])
And now your current create action should more or less work as expected, because now it's idomatic rails.
You still aren't going to get the new action with the same query params, but since you are taking those params and filling them in hidden fields on the form, they won't be lost when you try and fail to create. In any case, unless you do something to persist the values between requests, the POST to /attachments is going to wipe out the ery params.
Try this.
Replace
return false
With
redirect_to request.referrer || root_url
Note: root_url here is a catchall. Also this is Rails 4, I do not know if it also applies to Rails 3. Worth a try, though.
Debug ideas
First confirm a simple redirect_to root_url (or whatever name you use for your root) works in your controller
redirect_to root_url
Then, once redirect_to confirmed working, focus on getting the REST interface "request." information. There's a Rails 3 discussion here which may help you.
How to get request referer path?

Redirect to after successful ajax form

I've got a form with remote => true.
And right now my controller looks like:
# POST /items
# POST /items.json
def create
#item = #store.items.build(params[:item])
respond_to do |format|
if #item.save
format.html { redirect_to edit_admin_item_path(#item), :flash => {:success => "#{#item.name} was successfully created."} }
format.js { render :js => "window.location.href = ('#{edit_admin_item_path(#item)}');"}
format.json { render json: #item, status: :created, location: #item }
else
format.html { render action: "new" }
format.js { render :partial => 'fail_create.js.erb', :locals => { :ajax_errors => #item.errors.full_messages } }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
Which works but feels very clumsy. It also doesn't allow me to use a flash notice, which is sad time indeed.
Ideally I feel like I should be able to simply use "format.js { redirect_to...} or check against the request headers and redirect_to. Sheesh!
I'm not sure what the best solution is. Any advice would be super awesome, thanks in advance!
-- PS -- I know this has been asked somewhat before but to no avail: How to redirect after a successful AJAX form submission. There seems to many questions similar floating around, but no real solutions.
I think it might be impossible. The response to a Ajax request is processed by XMLHttpRequest. If a 3xx response is returned, XMLHttpRequest will follow the redirect itself, if the URL is of same origin. No matter how you set the headers, the browser cannot be aware of that. So the only way could be changing window.location with some Javascript.
I use a combination of Rails responders to generate my response messages and some content in my <action>.js file.
The content of — say update.js would look something like this:
// Checks if the article slug has changed.
// If it has the entire page should be reloaded at that new location.
<%= reload_if_slug_changed #article, params[:id] %>
// Displays the flash notices
// See ApplicationHelper#js_flash_response
<%= js_flash_response %>
Where the different methods are defined in some helper (in my case my ApplicationHelper). The content of the different methods are as follows:
def js_flash_response
if flash.now[:notice].present?
js = "$('#notice').html('#{flash.now[:notice]}').change();"
elsif flash.now[:alert].present?
js = "$('#alert').html('#{flash.now[:alert]}').change();"
end
end
def reload_if_slug_changed object, expected_value
"window.location.href = '#{url_for [:edit, object]}';" if object.slug != expected_value
end
The content of the flash messages are generated automatically by Rails responders and displayed with the now scope that deletes the from the flash hash, ensuring that if the user reloads (after the flash has been displayed) they will not reappear.
I don't believe that you should ever make a form pointing to a restful create action a remote one, because you would always expect critical redirect, so in my case I only need to redirect if the url slug has changed.
I hope that this helps. It's not a solution, but simply the way that I handled some of the same problems.
Best regards.
Under your scenario, here's how I would inject javascript into the page from a controller action. After you've completed the logic section of your action insert something like this:
render :update do |page|
page << "javascript_here"
end
This should allow you to insert you window.location or create a javascript flash method and call it when your create method executes correctly.
If you're looking to DRY up your controller actions, I would recommend looking into this Railscast about make_resourceful. Make_resourceful automagically performs each core activity for each action. It also allows you to tap into the hooks that they've created such as before :create, after :create, response_for :create, and after :create_fails. By using this gem, you can run code based on the success or failure of your methods and have finer grained control over them.
In addition to this, you should be able to initialize a create.js.erb and create_fails.js.erb in your view file, include a format.js without anything passed to it in your controller, and Rails will automagically run that file that contains javascript depending on if the controller action executed successfully.

How to return correct HTTP error codes from Ruby on Rails application

I have RoR 3.0 web application which is acting as an OAuth API provider. Now, in API I'd like to return correct HTTP error codes to the API consumer. How do I do this?
Here is example:
def destroy_oauth
#item = Item.find(params[:id])
if(!#item.nil? && #item.user_id == current_user.id)
#item.destroy
respond_to do |format|
format.js
format.xml
end
else
raise ActionController::RoutingError.new('Forbidden')
end
end
So, in case of error I'm trying to return Forbidden 403 code. Still, when running this I'm getting always 404 Not Found returned. How do I return the correct code?
Or is this somehow webserver configurable thing?
When you're just giving a status code and there is no body, a convenient way is
head 403
This method also accepts the symbolic names for status codes, such as
head :forbidden
You should render page with correct status.
render(:file => File.join(Rails.root, 'public/403.html'), :status => 403, :layout => false)
According to ActionController::Head docs just use this pattern in actions
return head([status]) if/unless [some condition here]
Example:
return head(:gone) if #record.deleted?
return head(:forbidden) unless #user.owns?(#record)
return is used to make sure that no remaining code in the action will be run.
I think you have two problems here: first is that your #item = Item.find(params[:id]) line is raising 404 and execution never gets to where intended (if statement). Second is that you are raising exceptions and never catch them. Try:
def destroy_oauth
begin
#item = Item.find(params[:id])
if(!#item.nil? && #item.user_id == current_user.id)
#item.destroy
respond_to do |format|
format.js
format.xml
end
else
raise ActionController::RoutingError.new('Forbidden')
end
rescue ActiveRecord::ResourceNotFound
redirect_to :action => 'not_found', :status => 404 # do whatever you want here
rescue ActionController::RoutingError
redirect_to :action => 'forbidden', :status => 403 # do whatever you want here
end
end
Something along those lines, but you also mentioned that you are building the API, so when you are rescuing the error, you may want to render xml error info. Something like:
# in application_controller.rb
rescue_from ActionController::RoutingError, :with => :render_forbidden_error
private
def render_forbidden_error(e)
render :status => e.status, :xml => e
end
Good luck. Udachi.
well, you can use
:status =>500
But, In default Rails take care of the error type rendering itself.
Errors default pages are in the public directory. 500.html,404.html etc..
For more information on :status , how to use it click here

How do I make a json request to a rails 3 website?

I know that if I have a url like
mysite/posts/1
The default type returned to me is html. I can get an xml version of the resource by just doing
mysite/posts/1.xml
But how do I get a json version? Is the following supposed to work?
mysite/posts/1.json
Reason I ask is because it doesn't seem to be working. So I figured I should find out if it's "supposed" to work this way before investigating further.
You're doing it right, but if the Controller isn't setup to respond to json requests you won't get anything. You'll have a respond_to block like this:
respond_to do |format|
format.html
format.xml { render :xml => #model_var.to_xml }
format.json { render :json => #model_var.to_json } #without this line, .json requests will go unanswered by the web server.
end

Resources