I'm working on exposing an API using RABL. Exposing the views is easy enough, but I've run into trouble having my own application consume those views.
For example, assume I have an endpoint at http://example.com/api/articles, which produces a JSON representation of the articles.
In my ArticlesController#index action, I wish to render a (HAML) view that shows a list of the articles. Rather than duplicate the logic from ArticlesApiController#index, I want to simply use that data—i.e., I want to say "go get this data from the /articles API endpoint," and then pass that data to the HAML view.
How can I do that? Or, is this the wrong way to go about doing this?
Watch this screencast on RABL.
In your view you can add the data from RABL as follows(taken from railscast sample):
<div id="articles"
data-articles="<%= render(template: "articles/index.json.rabl") %>">
Note
You can make the data access even more simple by using the gon gem.
In your controller
def index
#articles = Article.all
# initialize the JS variable upon HTML request
gon.rabl if request.format == Mime::HTML
end
Now you can access the data in the javascript as follows:
alert(gon.articles)
Related
I'm developing a simple app that teaches people english. The app is based on 5 modules of 34 classes each - 170 total. Each class has its own html page.
Since i dont want to create a view for each class, i scaffolded an Aula model ("class" in portuguese) and saved the html of each class in the model's DB, so i could use only the standard Show view paths to show the classes using their individual id's.
Controller code:
def show
#aula = set_aula
end
These HTML pages are being stored in the database as strings and then being outputted on the Show view using the html_safe method.
#show view code:
<%= #aula.aula.html_safe %>
#"aula" is the DB attribute with the html of each class
It rendered the HTML with no problems, and i got what i wanted. But since i'm creating a rails app, i decided to use embedded Ruby code like <%= link_to %> and <% image_tag %> mixed with the HTML of the classes to create links and show images, and the problem is that these links are being outputted as strings as well, just like any other line, instead of being read and executed as actual Ruby code.
I've been doing a lot of research, but so far I can't find exactly how to make the ERB code be read properly.
Maybe I need to save the HTML in the DB using another data type, or I need to use another method to render the HTML.
First, I'll answer your question, then make a suggestion that you think very carefully before using this approach.
The answer in the post https://stackoverflow.com/a/14351129/483133 shows how to render ERB directly from stored HTML text. Modifying this, here is some code you could use:
def show_html
html = #aula.aula
template = ERB.new(html)
template.result.html_safe
end
# Run this from your controller action, for example, with
def show
#aula = set_aula
end
# inside your view show.erb.html
<%= show_html %>
Warning
I would strongly recommend against finding a solution that allows Ruby code stored in the database to be run. If the pages are able to be written in any way by end users, rather than trusted software developers, then you have opened a huge security hole. Any Ruby code could be run on your server.
I would suggest you consider using a client-side rendering solution (such as Handlebars: http://handlebarsjs.com/ ), which allows for basic rendering of data dynamically in HTML, while not allowing code to be run on your server.
I'm writing a rails application with an AngularJS front-end, this is part of a tutorial series I'm writing on connecting rails and angularjs. This means my rails application communicates with the browser exclusively in JSON.
In the angularjs $http documentation it describes a potential json security vulnerability where the json request can be embedded into a script tag, plus some tricky use of jsonp, to allow something akin to a cross-site scripting attack. I've found a few other pages, one in particular I thought described this well, and dates from 2008, so this isn't a new issue.
Apparently this isn't a vulnerability in standard rails json rendering, as rails by default provides back an object containing an array. But when working with angularjs we appear to set root: false (although I have to confess I can't find where I did that, but it's definitely not giving the root node).
Anyway, the bottom line is that the angular documentation recommends prefixing any json response with )]}', so:
['one','two']
Becomes
)]}',
['one','two']
Angular then automatically strips that off again.
I'm looking for a way to do this elegantly. I've seen a lot of questions and answers on stackoverflow about this, but most of those either relate to much earlier versions of rails before JSON handling was more thoroughly embedded, or seem to require me to create a lot of boilerplate code. I'm looking for a method that I can apply to the application controller, or as a helper method, that will work everywhere.
The controller that I'm currently using looks as follows:
class ClubsController < ApplicationController
respond_to :json
# GET /clubs.json
def index
#clubs = Club.all
render json: #clubs
end
end
This doesn't call any templates - the render action skips the templating engine. I can get this working by changing the render line instead to:
respond_with json: #clubs
And creating a template file views/clubs/index.json.erb that contains
)]}',
<%= raw(#clubs.to_json) %>
But I'd then have to create a template for every action on every controller, which feels like boilerplate. I'd like instead to be able to change views/layouts/application.json.erb to have something like:
)]}',
<%= yield %>
But that doesn't work because we only get templating if we call respond_with. And if we call respond_with, we have no way to put the #clubs into the response - so we end up with:
)]}',
As the entirety of the response.
An alternative would perhaps be to override the as_json method to prepend what I want, but that seems a bit like a sledgehammer. Ideally there would be a place I could introduce a helper method, something like:
render prepend_vulnerability_protection(json: #clubs)
So, after all that, two questions:
Is this even a real problem, or does Rails already have some other protection that means I don't need to worry about this at all
Is there a way to do this centrally, or do I need to bite the bullet and create all the boilerplate templates? I can modify the scaffold generators to do it, so it's not the end of the world, but it does seem like a lot of boilerplate code
So, no responses as yet. I'm going to write down what I find from my research, and my current answer.
Firstly, I think this is a genuine vulnerability in rails. Unfortunately the rails and JSON/JSONP area has had some other recent vulnerabilities relating to the JSON parser at the Rails end. That has really drowned out any google search relating to this specific XSS issue.
There are a couple of approaches to resolving this:
Have your application only respond to put/post/delete requests. That's not really an option when integrating to Angular - well, it is, but it means overriding a bunch of standard behaviour
Insert something at the front of your returned JSON - this can be the root node (default rails behaviour in rails 3, no longer in 3.1), a closure like )]};, or a loop like while (1);. Angular expects and can deal with )]}',
I've looked at using a json template in my rails app. You can do this with one of many gems, the one I like the look of is JBuilder (railscast 320), but RABL is perhaps more powerful (railscast 322).
This does mean a template for each of the actions on each of the controllers. However, I've also just completed working out how to have rails scaffold those for me automatically, so it's not as scary as it was when I first asked the question, and I can see some other reasons that I might want more control over the json that is returned from my application.
Having said that, I couldn't immediately see a way to get JBuilder to prepend an arbitrary string - it seems to only want to prepare valid JSON (and this I think is not valid JSON). RABL looks like it can do it, but it is a bit more complex. It can definitely be done through just using ERB, but I feel kinda wrong in doing that.
The other alternative I've identified is a helper method in application_controller.rb, which I then call in each of my controller methods. This is reasonably elegant, and I can quite easily change my template to do it. So I'm going with this for now:
class ApplicationController < ActionController::Base
def render_with_protection(json_content, parameters = {})
render parameters.merge(content_type: 'application/json', text: ")]}',\n" + json_content)
end
end
class ClubsController < ApplicationController
respond_to :json
# GET /clubs.json
def index
#clubs = Club.all
render_with_protection #clubs.to_json
end
# GET /clubs/1.json
def show
#club = Club.find(params[:id])
render_with_protection #club.to_json
end
# POST /clubs.json
def create
#club = Club.new(params[:club])
if #club.save
render_with_protection #club.to_json, {status: :created, location: #club}
else
render_with_protection #club.errors.to_json, {status: :unprocessable_entity}
end
end
end
Note that you should be also including CSRF protection in your application controller - so see this as additive to the security precautions you were already taking, not a replacement.
Please explain why do we need this code in a controller? What is the significance of this block of code?
respond_to do |format|
format.html # index.html.erb
format.json { render json: #users }
end
It allows you to format output differently depending on the format the user/caller requests. If you were to access http://yourhost/controller/index.html, the controller would respond with the ERB template index.html.erb (or HAML or whatever). If you were to access http://yourhost/controller/index.json, it would respond with the JSON template index.json.erb.
This allows you to have a single controller action that can prepare data and then select the view for rendering based on the requested format.
Defines mime types that are rendered by default when invoking respond_with.
So basically, this means that your controller action can be hit in different formats(html, json in your case), and still provide data back to whatever is calling it. This is helpful for API development, and many other things.
For example: You want to get a json list of all your users to do something with javascript. You'd call /users.json and this would go to your user_controller#index action and know to render a json object of all your users.
The above code is scaffold generated, and provides a way to render *.html and *.json views for your controller, making it easy to have access to data for implementing an API or normal views for your web application.
You can also create XML output:
format.xml { render xml: #users }
and other formats like PDF, or DOC, depending on the gems you are using.
See the Rails Guide Action Controller Overview for more information.
I'm thinking about this since a while: I'm build a single-page webapp with ExtJS library.
The whole GUI will be handled with JSON through AJAX requests, so I effectively never use the view functionality (except for the main page, where the view is six lines just to write basic tags).
However, I struck in a problem which I think will happen often during my application: I have a complex json which needs a lot of view helpers to be built, expecially for referencing other resources like images. These json objects are statically written by me, for example I need to configure ext to render some desktop-like icons which needs title and a reference to an image. I think this should be written as a view where params are an array of hashes containing title/image.
That being said, I were thinking about an approach where my json objects will be built by the view, and not by the controller as I'm currently doing.
My questions is:
Is this approach OK? I feel like violating MVC pattern when I try to use image_path helper inside my controller.
It's important to understand that I'm not trying to fetch something in the view, I'm just passing some model objects as params to views and write them in a json fashion. I can't (always) use .to_json method because sometimes I need to organize those json objects in a totally different way.
Edit 1:
I'm adding a small question that could become really useful with this approach: is possible to parse (in the controller) a YML.ERB file in the controller but allowing it (the yml) to use all those nice helpers that I have in html.erb files? I would like to use them because is nicer to build some static json objects in yml rather than plain json. If that's the case, how to do it? Remember that I'm in a controller. Thanks a lot.
You can easily write json directly in your view templates, just like you would be writing erb, or xml. E.g. if you have a controller like this
class FoosController < ApplicationController
def index
end
end
And you accept format in your routes, then you can simply create a view
<% # app/views/foos/index.json.erb %>
{ some_json_stuff: "<%= link_to 'home', root_url %>" }
This view will be rendered when you access /foos.json. This enables you to write any custom json with helpers, partials, etc.
You might want to check out Draper, which is a gem that provides view models for Rails. It is ideal for things like JSON views, which arguably have no place in the model.
It also allows you access to helpers and the request context.
class ArticleDecorator < ApplicationDecorator
decorates :article
def as_json(optsions={})
{
:id => model.id,
:foo => helpers.some_helper_method,
:secret => helpers.current_user.admin? ? "secrets!" : "no secrets"
# ...
}
end
end
In my current project I have a couple instances where I have a re-usable form that exists inside a rails partial. This form submits to a specific controller via ajax (:remote => true). The controller does some stuff and then returns back the appropriate js.erb to modify the page via javascript.
This works fine for when I have a single view. But the problem seems to happen when this re-usable partial exists on multiple views. In view 1 I might want to issue a completely different set of javascript commands then in view 2.
As a concrete example, say I have a comments controller that has the normal CRUD operations.
I now have partial called _comments_box.erb. This _comments_box.erb contains the ability to submit a comment via a simple line:
- form_for comment, :url => post_comments_path(post), :remote => true do |f|
This submits to a comments_controller.rb create method which looks somethings like this:
def create
... do some stuff, like create a new comments model
respond_to do |format|
# will respond with create.js.erb
format.js
end
end
The create.js.erb in turn adds a comment to the view, perhaps doing a bunch of other updates to the DOM.
Say I render the _comments_box.erb within a view called post_summary.erb. Now I have another view, post_detail.erb that requires the same _comments_box.erb. However the post_detail.erb requires me to update completely different divs on the DOM in response to a new comment.
I need to create a different JS response for each instantiation. So I can either:
Create an alternate controller method, say create_2. Pass in some parameter to the _comments_box.erb from post_detail.erb to the _comments_box.erb partial so it knows which controller method to fire. This will allow me to have a separate file _create_2.js.erb that will allow me to manipulate the post_detail.erb view independently.
Forget about using js.erb altogether and just use plain old AJAX and get back JSON, and handle the javascript manipulation completely on the client-side.
It seems option 1 allows me to continue to use the UJS supported by Rails which is nice. But also means I probably will be adding a lot of duplicate code everywhere which is annoying. Is there a way for me to do this elegantly while continuing to use UJS?
That's exactly the purpose of Apotomo: http://apotomo.de/
Here is it's own description:
Apotomo is a true MVC widget framework
for Rails. Widgets are based on Cells
and provide reuseable view components.
Having bubbling events, they know when
and how to update themselves via AJAX!
Working with Apotomo widgets almost
feels like developing GUI components –
in a Rails environment.
Have a try, it's great.
I'd not recommend using UJS for frontend apps: server shouldn't take care of client side business. I agree it's useful and clean but it lacks performance and thus should be kept for backend stuff (RJS will move into a gem, see here: http://weblog.rubyonrails.org/2011/4/21/jquery-new-default).
That said, back to the solutions you expose:
1) I think you won't need an extra controller, you'd just have to pass additional params in order to know from where to query came from. A hidden_field could do the trick. With this info, render the good js.erb file
format.js { if condition
render "create.js.erb"
else
render "create_2.js.erb"
end
}
2) I'd go for it and return json but you'll face the same problem: knowing from where the request comes from.
A better solution (than using a hidden_field) might be to check the request.referer in your controller action. This way you leverage the fact that each context has a unique URL, and don't have to explicitly specify another unique value when rendering your widget partial.