How to force RABL to output XML as default - ruby-on-rails

How do I force XML output with RABL? It renders JSON as default, which works fine. When I access my page with
curl -H "Accept: application/xml" -X GET (myurl)
RABL renders XML as expected. However, when I access the same URL with a browser, the response is in JSON.
I tried setting
ActionView::Template::Handlers::Rabl.default_format = Mime::XML
as well as
render "index.rabl", :content_type => 'application/xml'
In that case, rails returns application/xml as content type but still returns JSON.
How can I render the XML representation of my data without sending application/xmlas the only accept header?

Try adding a default format to your route:
get 'your/url/matcher' => 'controller#method', :defaults => { :format => :xml }

What version of Rails are you on? I am pretty sure if you do this for instance:
respond_to do |format|
format.xml # renders rabl as xml
format.json # render rabl as json
end
But that's in Rails 2. I am sure there is an equivalent for 3.

Related

Convert html response to json

I am trying to implement SMS API in rails, when I hit the API url I get following html response:
<!DOCTYPE RESULT SYSTEM "http: api_link ">
<html><body>
<result reqid="57469">
<mid submitdate="2015-12-02 00:51:55" id="1" tag="null" tid="103335">
</mid>
</result>
</body></html>
How can I convert this to json?
Unfortunately at this time you cannot convert html straight to json, but you can convert html to a hash and then the hash to json.
YOUR_HTML_CODE = '<!DOCTYPE RESULT SYSTEM "http: api_link "><html><body> <result reqid="57469"> <mid submitdate="2015-12-02 00:51:55" id="1" tag="null" tid="103335"> </mid></result></body></html>'
#data = Hash.from_xml(YOUR_HTML_CODE).to_json
Returns:
=> "{\"html\":{\"body\":{\"result\":{\"reqid\":\"57469\",\"mid\":{\"submitdate\":\"2015-12-02 00:51:55\",\"id\":\"1\",\"tag\":\"null\",\"tid\":\"103335\",\"__content__\":\"\\n\\n \"}}}}}"
To find more ways to do what you want, search for rails xml to json, you will find more answers on this question. Crack gem seems to be excellent for this.
Note that if you are using rails you should be handling this on the backend, but that was not as helpful of an answer by itself, thus my answer above.
If your api only returns json you can set format resource.
Example:
config/routes.rb
Rails.application.routes.draw do
resources :posts, defaults: { format: :json }
end
If your api returns html, xml,and json you can set in your controller(remove defaults: { format: :json } in config/routes.rb):
app/controller/posts_controller.rb
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #post }
format.json { render :json => #post }
end

Route interprets JSON request as HTML?

Using Ruby 1.8.7 and Rails 3.2.12.
I am experiencing an issue when testing URLs with a ".json" extension. I'm building custom error pages and have the following:
# errors_controller.rb
def show
#exception = env["action_dispatch.exception"]
respond_to do |format|
format.json { render :json => { :error => #exception.message, :status => request.path[1..-1] } }
format.html { render :file => File.join(Rails.root, 'public', request.path[1..-1]), :format => [:html], :status => request.path[1..-1], :layout => false }
end
end
# routes.rb
match ":status" => "errors#show", :constraints => { :status => /\d{3}/ }
# application.rb
config.exceptions_app = self.routes
For URLs such as "localhost:3000/session/nourl.json", I trigger the HTML block of respond_to, and I can verify that the server responds with the HTML format with these logs:
Processing by ErrorsController#show as HTML
Parameters: {"status"=>"404"}
Rendered public/404.html (13.2ms)
Completed 404 Not Found in 48ms (Views: 47.3ms | ActiveRecord: 0.0ms)
The only way I've been able to trigger the JSON block is with :format => :json in the route, then it works fine but "localhost:3000/session/nourl" would respond with JSON too.
It feels like I am doing something foolish here because I've seen other examples of both cases being triggered in the expected way and I see absolutely no other cases of similar behavior, so I'm compelled to think this is an isolated situation or it's some cascading issue that I cannot observe or am causing elsewhere.
If anyone could provide some insight on potential issues I would be appreciative.
Updated:
A little more info: If I query something like "localhost:3000/locations/1.json", I get the expected response; a JSON formatted page with the object details. I can't achieve this same behavior when requesting arbitrary URLs with a ".json" extension and attempting to format a custom JSON response to return. Is there a way to do this?
Rails delegates the call to the Error-Application where all the request-format stuff gets lost. So you will need to check that on your own. You could check on the request information like this:
def api_request?
env['ORIGINAL_FULLPATH'] =~ /^\/api/
end
def json_request?
env['ORIGINAL_FULLPATH'] =~ /\.json$/
end
Read more about this approach here: http://phillipridlen.com/notes/2012/12/13/returning-multiple-formats-with-custom-dynamic-error-pages-in-rails/

How do you handle bad formats in Rails 2 routing?

How does one handle bad formats in routes in Rails 2.3? For instance suppose that you have an action that wants to handle html or json requests but nothing else how do you restrict it while allowing user-readable errors to be promulgated? The following snippet shows a start:
respond_to do |format|
format.html # render the default
format.json { do something appropriate }
format.all ?
end
The trouble is what to put in place of the ?, I tried:
format.all :text => "That's a bad format.", :status => 406
and while the status code got set appropriately the text does not get rendered (at least with a format like com, which is one that I'm receiving.
One possibility would be to change the routes file so that only the two formats were accepted, but that runs into route explosion. (I have 4 acceptable formats.) The idea of using
map.connect '/xyz.:format', :action => ..., :controller => ..., :format => '/html|json/'
sounds good but doesn't work -- it matches something like xyz.comhtml. I'm frustrated and hoping there's something I'm missing.
I might be wrong but I think for your format.all calls you can pass it a file in return.. something like this where you define the return type as well:
format.all { render :file => File.join(Rails.public_path, '406.html'), :status => 406, :content_type => 'text/html' }
And just put a "406.html" file in your public directory with that text "That's a bad format." in it.

How to render sitemap.xml in rails app

I have added /views/sitemap/index.xml and want it displayed when i go to the relevant url.
class SitemapController < ApplicationController
def index
respond_to do |format|
format.html
format.xml
end
end
end
And in routes.rb
match "sitemap/" => "sitemap#index"
Using Rails 3
When I go to mydomain.com/sitemap/ I just get a white page. Any ideas?
index.xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<url>
<loc>http://www.mydomain.com/</loc>
<changefreq>weekly</changefreq>
</url>
</urlset>
Problem is that you are using your index action to render xml and it will render "index.xml"
file not "sitemap.xml" which is what you have created in your views
While your routes are correct, you are using the wrong filename in views
Try renaming sitemap.xml file to index.xml ( in the views/sitemap folder)
If you define name routes, you need to define :format with it
match "/sitemap/sitemap.[:format]", :to => "sitemap#index"
it will pickup your format from there. Also you can define a default format in the routes
match "sitemap/sitemap.xml", :to => "sitemap#index", :defaults => {:format => :xml}
I may be wrong , but I see 2 reasons:
index action doesn't actually do anything judging by this code sample, it just responds back with no info.
you need to render your object as xml - if you don't rails, doesn't know you want xml - it just treats it as another file extension. It actually lets you do little tricks - like sending json to an xml request ( thou I have no idea why would anyone try to do that). Thou one useful application is that you can make rails send custom rendering of an object to a common format or render regular data in common format for an unusual extension ( we had a client who wanted csv data for a .dat request)
Here is a short example, from a sample home controller:
class HomeController < ApplicationController
def index
#m = {
:color => "yellow",
:total => "20"
}
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #m}
end
end
end
this returns this object as xml:
<hash>
<total>20</total>
<color>yellow</color>
</hash>

How do I override the RJS MIME Type in Rails 2.3?

I have an app running Rails 2.3.5 that has a JSON API for much of it.
A contractor came in and did some work on the app and used RJS in a few places. Some of those controller actions that are using RJS for the main web site also need to be part of the API.
The problem is that JSON API requests trigger the RJS responses, which is not what I want. I'd like the RJS responses to happen from a browser, but when it's an API request (as distinguished by using the "application/json" Accept and Content-Type headers) then I'd like it to just send the API response.
From what I can tell, Rails triggers an RJS response for any MIME Type that involves javascript, i.e.
text/javascript
application/json
etc.
Is there a way to force RJS to only respond to text/javascript? Or is there a better way to solve this problem?
To make it more clear, I have code that looks like this:
def show
#dashboard = #user.dashboard
respond_to do |wants|
wants.html
wants.json { render :json => #dashboard }
end
end
The trouble is, there is also a show.rjs template in the view folder for this controller. When someone hits the API, I want it to render the json results, as I have listed above, but instead, it renders the show.rjs template.
How can I make sure that API clients get the json results I want, and still let the RJS template render for people on the website?
You will need json defined in your mime_types.rb and then you should be able to do this:
def show
#dashboard = #user.dashboard
respond_to do |format|
format.html
format.json {render :json => #dashboard}
format.js
end
end
More to read here: http://ryanbigg.com/2009/04/how-rails-works-2-mime-types-respond_to/
In your controller's action try the following:
def index
respond_to do |format|
format.html # Renders index.html.erb as usual
format.xml { render :json => {:name => "raskchanky"}.to_json }
end
end
According to Rails documentation (http://api.rubyonrails.org/classes/ActionController/Base.html) "render :json" sets the content type of the response to application/json.
Are you sure that your javascript is sending the correct headers? When I do rjs, generally I do this by responding to format.js. This allows my to easily separate json and js responses.
Generally, my problems have been in ensuring that my ajax actions are actually sending requests in the proper format. When in doubt, you can add a 'format':'js' parameter to your request. In jQuery:
// try to get it to figure out rjs actions by itself
$('a').click(function(e){
e.preventDefault();
$.get({
url: this.attr('href'),
dataType: 'script',
success: responseFunction
});
});
// or force the format
$('a').click(function(e){
e.preventDefault();
$.get({
url: this.attr('href'),
data: {format: 'js'},
success: responseFunction
});
});

Resources