why "Completed 406 Not Acceptable" has gone after removing respond_to? - ruby-on-rails

when I used $.ajax() to submit the form's content with the following respond_to block:
respond_to do
render :json => { :url => question_path(resource, :recent => :true),
:uploadPath => question_path(#question, :format => "json"),
:editPath => edit_question_path(#question),
:exitPath => question_path(resource)
}
end
I got a error:
Completed 406 Not Acceptable in 124690.7ms (Views: 0.4ms | ActiveRecord: 16.7ms | Sphinx: 0.0ms)
After I removed respond_to, as the above code is changed into:
render :json => { :url => question_path(resource, :recent => :true),
:uploadPath => question_path(#question, :format => "json"),
:editPath => edit_question_path(#question),
:exitPath => question_path(resource)
}
The error has gone, why this happens? what things I miss? Thank you!

respond_to is used when you want to produce different responses for the same controller action. For example perhaps you want to render HTML for browsers but JSON for an API client. You list all the responses you can generate (and how they should be generated) and rails picks the one that matches the request:
respond_to do |format|
format.html { ... }
format.json { ... }
end
In the example above rails would know how to generate HTML and JSON responses but if a request came in requesting an XML response then rails would produce a 406 error (not acceptable ) because it doesn't know how to produce such a response.
In your code snippet you are calling respond_to so you are telling rails to only use the response formats given by the block but you are not defining any response formats (you're not using the object yielded to the block), therefore rails produces a 406.
When you remove the call to respond_to Rails no longer worries about trying to pick the correct response format and just uses your call to render (and would do so no matter what format the request was asking for).
If you wanted to use respond_to you would do
respond_to do |format|
format.json { render :json => { ... }}
end
when doing so you do need to ensure that the request is requesting json - if rails though it was requesting HTML then you'd be back to your 406 error. You can use jQuery's dataType option for this or use an extension of .json (ie make your request to /foo/bar.json)

Related

My rendering in Rails (possibly) causes a 422 error

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.

Postman multipart POST to Rails

I'm trying to send a POST request from some client to a rails server and I'm having some problems.The full requirement is to send an image to to be processed by paperclip but it look like it's a general postman multipart POST with Rails problem.
This is what I'm getting:
Bellow my setup:
class CategoriesController < ApplicationController
def create
#category = Category.new(category_params)
respond_to do |format|
if #category.save
format.html { redirect_to #category, notice: 'Category was successfully created.' }
format.json { render :show, status: :created, location: #category }
else
format.html { render :new }
format.json { render json: #category.errors, status: :unprocessable_entity }
end
end
end
private
def category_params
params.require(:category).permit(:label, :description)
end
I'm assuming the problem is that the Request params are not encapsulated int the "categories".
Please let me know if I wasn't clear enough and if I can offer more info.
Thanks in advance.
EDIT:
As suggested by fylooi I've changed the Request Body in Postman adding an encapsulating "entity" like this:
Still I'm getting the same results
Processing by CategoriesController#create as JSON
Parameters: {"------WebKitFormBoundaryFdJXZFMuAl0fZf3Q\r\nContent-Disposition: form-data; name"=>"\"category[label]\"\r\n\r\nTraffic\r\n------WebKitFormBoundaryFdJXZFMuAl0fZf3Q\r\nContent-Disposition: form-data; name=\"category[description]\"\r\n\r\nTraffic category\r\n------WebKitFormBoundaryFdJXZFMuAl0fZf3Q--\r\n"}
Completed 400 Bad Request in 1ms (ActiveRecord: 0.0ms)
ActionController::ParameterMissing (param is missing or the value is empty: category):
app/controllers/categories_controller.rb:67:in `category_params'
app/controllers/categories_controller.rb:27:in `create'
Postman works fine with Rails, you just need to understand how Rails handles parameters in general.
Let's say you POST the following parameters to the server:
plain_param=value
nested_object[attribute]=value
This gets parsed into the following:
pry(main)> params = ActionController::Parameters.new(plain_param:"value", nested_object: { attribute: "value" } )
=> {"plain_param"=>"value", "nested_object"=>{"attribute"=>"value"}}
Let's take a look at how permit works.
params.permit(:plain_param)
pry(main)> params.permit(:plain_param)
Unpermitted parameter: nested_object
=> {"plain_param"=>"value"}
pry(main)> params.permit(:nested_object)
Unpermitted parameters: plain_param, nested_object
=> {}
pry(main)> params.permit(:nested_object => :attribute)
Unpermitted parameter: plain_param
=> {"nested_object"=>{"attribute"=>"value"}}
pry(main)> params.permit(:plain_param, :nested_object => :attribute )
=> {"plain_param"=>"value", "nested_object"=>{"attribute"=>"value"}}
So far, so good. Looks like permit returns the entire hash for top level and nested permitted keys through and prints an alert for unpermitted keys. How about require?
[33] pry(main)> params
=> {"plain_param"=>"value", "nested_object"=>{"attribute"=>"value"}}
pry(main)> params.require(:plain_param)
=> "value"
pry(main)> params.require(:nested_object)
=> {"attribute"=>"value"}
pry(main)> params.require(:nested_object => :attribute)
ActionController::ParameterMissing: param is missing or the value is empty: {:nested_object=>:attribute}
pry(main)> params.require(:plain_param, :nested_object)
ArgumentError: wrong number of arguments (2 for 1)
We can see that require returns the value for a single param key. This comes in handy to ensure the presence of objects with multiple attributes.
Wrapping up:
params.require(:category).permit(:label, :description)
expects a hash of the form
{:category=>{:label=>"value", :description=>"value"}}
which translates to HTML POST parameters of
category[label]=value
category[description]=value
Edit: Postman automagically sets the content-type header for multi part file upload, so do not set it manually. Not sure whether this is considered a bug or a feature.
https://github.com/postmanlabs/postman-app-support/issues/191

Internal server error 500 on missing image file (Rails 3.2.12)

If we request a bogus image file, Rails generates an internal 500 server error instead of a 404. See the log below.
Here is the line in routes.rb that catches 404s:
# Catches all 404 errors and redirects
match '*url' => 'default#error_404'
Other unknown URLs are handled correctly with 404s. What is different for image files and URLs with file extensions?
Started GET "/images/doesnotexistyo.png" for 71.198.44.101 at 2013-03-08 07:59:24 +0300
Processing by DefaultController#error_404 as PNG
Parameters: {"url"=>"images/doesnotexistyo"}
Completed 500 Internal Server Error in 1ms
ActionView::MissingTemplate (Missing template default/error_404, application/error_404 with {:locale=>[:en], :formats=>[:png], :handlers=>[:erb, :builder]}. Searched in:
* "/home/prod/Prod/app/views"
The problem is that the error_404 method inside the Default controller can't handle requests in png format. When you ask for say, a JSON response, you could build an URL similar to:
/controller/action.json
And inside the action you would have something like
def action
respond_to do |format|
format.html # Renders the default view
format.json { render :json => #model }
format.xml { render :xml => #model }
end
end
As you can see, it's specified how to handle a JSON and an XML request, but since there's no format.png, the action can't handle the .png format. Add this:
format.png # Handle the request here...
Hope it helps :)
Edit
Add this to redirect to your 404 handler:
def error_404
respond_to do |format|
format.html
format.png { redirect_to :controller => 'default', :action => 'error_404' }
end
end
Cheers :)
Edit2
Use this code to catch all kinds of requests:
def error_404
respond_to do |format|
format.html { render :not_found_view }
format.all { redirect_to controller: 'default', action: 'error_404' }
end
end
Replace :not_found_view with your 404 page. This will render the 404 page for html requests, and redirect to self (with html format) for any other kind of request.
Hope it helps :)
What is DefaultController? That controller is dealing with the 404, instead of Rails default response:
ActionController::RoutingError (No route matches [GET] "/images/doesnotexistyo.png"):
So find out this controller, error_404 is being executed and no template default/error_404 was found, hence the 500 error.
You probably have a code similar to this somewhere in your code:
rescue_from ActiveRecord::RecordNotFound, :with => :error_404
Maybe not for you, but since I do some final checks for pages dynamically in my controllers, I just follow all my 404'ing with one to handle non-html files:
format.all { render :status => 404, :nothing => true }

Implement a Rails API

Am working in rails. If I send a request like http://www.example.com/titles/search?search_text=something
as a respond I need a Json .How to implement this in Rails?
If you want a response in JSON format then you need to pass your request as url.json format.
In your controller, inside that action check your request format and return the result according to the requested format. You can read more about this here and having example source code as wel. Check it out here Rendering JSON and other formats.
respond_to do |format|
format.html {}
format.json {
render :json => #your_obj.to_json, :callback => params[:callback], :content_type => "text/html" }
format.xml { render :xml => response }
end
In your titles contoller in particular action, having a search_text as get param use
render json: #object
detailed: http://guides.rubyonrails.org/layouts_and_rendering.html#using-render (2.2.8)

How to return truly empty body in rails? i.e. content-length 0

I'm trying to return a 200 status with an empty body but rails returns a body with a single space. i.e. content-length 1
For instance, this code generates a body with the single space
respond_to do |f|
f.html {head :ok}
end
and so does this
respond_to do |f|
f.html {render :nothing => true}
end
Yes, even render :nothing generates something.
All of this seems to be flowing from a 2005 patch in rails that was designed to fix a bug in Safari where it would ignore the headers if the body was empty. (http://dev.rubyonrails.org/changeset/1818)
Does anyone have any thoughts on how to get a 200 status but with a truly empty body? Background: I'm using an API that makes calls to my controllers. I need to send a 200 but the single space body causes the API to malfunction (parse error...). Also, I'll be deploying to Heroku so I can't patch ActionPack to undo the 2005 hack.
Thanks for any thoughts.
This appears to work
render :text => ""
For Rails 3, you could use:
render :nothing => true
In Rails 5, render :nothing => true is deprecated (planned for removal in 5.1)
Use
def action
head :ok
end
Credit to this question for the Rails 5 solution.

Resources