I've recently found the following code for nesting json in a rails app.
And I wonder, what's the preferred way of doing this?
Should I use active model serializers or stick to the following code?
git
class DrinksController < ApiController
...
# GET /drinks/:id
def show
#drink = Drink.find(params[:id])
render json: #drink.to_json(:include => { :ingredients => { :only => [:id, :description] }})
end
end
This is all going to depend on your use case. For example, if your app is mostly server side rendered HTML with a few javascript sprinkles, your current method maybe perfectly adequate.
However, if you are using a client side framework and using Rails mostly as an API endpoint you'll most likely want to look into a more robust solution that can better handle complex relationships and perhaps even presenter methods. As your app grows it will inevitably become more complex and to_json will quickly be outgrown.
Jbuilder is a great library for handling complex JSON rendering and worth looking into, if that's the case.
Related
I have a User model which gets accessed through a REST API and ActiveAdmin. Since there is a lot duplicated code (almost all) it might be a good idea to dry things up. There are a lot of topics out there handling this issue but i do not find a proper way. I put some of the ideas i have below
UserController instance in each controller
Again duplicated code gets created and the functions
#AA
controller do
def create
#userController = UserController.new
#userController.create(params)
end
end
#REST API
#Create instance through callback
def create
#userController.create(params)
end
Inheritance
I would like to have one basic CRUD controller lying above my model through inheritance like this
Class BaseUserController < ApplicationController
Class Api::UserController < BaseUserController
but (yet) i dont know how to do this in AA also in mind with the inherited_resource gem in mind
Concerns
Also concerns seem to be a nice way. But i would then create seperate concerns for each action to have one concern to exactly handle one behaviour
aspect and also to make them more reuseable . This then again seems to unnecessarily bloat the app and also concerns are not really made to be used like that. (?)
Modules/Mixins
Since modules cannot be instantiated but implement actions fully, this might just be what i am looking for. So each controller includes the desired actions or extend fro ma model rather than a class then.
The thing is - the more i read the more i get confused. I hope some of you guys might help me bring some light or i might turn to the dark side an use C# and .NEt again :D
Thanks in advance!
Example Code
I took this create method for a SMS resource in both, the REST API and the AA, where i call a couple of Services, but if complexity grows i have to add these lines in two places. How could i avoid that? Cant i have a basic SMSController which is used from the API endpoints as well as the AA interface?
controller do
def create
#customer =Customer.find_by_phone(params[:sms_message][:phone])
SmsService.sendSMS(#customer[:phone],Cryptoalgorithm.sms(#customer[:id], #customer[:recharge_token],params[:sms_message][:text].to_i)
#sms = SmsMessage.create(:customer_id => #customer[:id], :text => params[:sms_message][:text], :phone => params[:sms_message][:phone])
#customer.increment!(:recharge_token)
redirect_to admin_sms_message_path(#sms[:id])
end
end
I have the most basic of questions, but the more I think about it, the more complex it gets.
I've been using rails and it follows the MVC paradigm in that db and api calls are abstracted through calls generated through the controller. This seems way too heavy for what I want.
1) I want a simple (basic) web server that sits in front of my datastore. (The contents of which happen to be stored in a directory structure that follows: /LOCATIONS/LOCATION/PRESENTERS/PRESENTER/YEAR/MN/)
2) I want to be able to host json files within that directory structure and GET them as needed.
3) I want to be able to PUT/POST append to those json files.
Seems like all I'd need is nginx with my datastore as a doc root and index.html files at critical places within the structure (e.g. site.com/Locations/index.html , site.com/locations/SF/presenters/solomon/index.html)?
How would I begin to solve this problem, (without the use of controllers of coarse)?
MVC Frameworks
without the use of controllers
You must be aware that there are many more frameworks than Rails out there, so when you ask about using a system to "sit in front of your datastore", you're really looking for different frameworks to handle requests, of which there are many.
The problem you have is how do you keep data consistency, whilst ensuring you can handle the relevant API requests (through JSON). The answer is to look at how the systems you're examining work.
I can only really vouch for Rails (it's the only framework I've got production apps for) -
--
Rails
Creating an API in Rails is so simple - I don't know why you'd think about doing anything else
Using the MVC principle might seem bloated to you, but the security, structure and extensibility it provides is unmatched.
Here is how to create an API in Rails:
#config/routes.rb
namespace :api do
resources :controller, only: [:update, :create] #-> only PUT / POST
end
#app/controllers/api/your_controller.rb
class API::YourController < ApplicationController
respond_to :json
def update
# handle PUT request
end
def create
# handle POST request
end
end
#app/models/model.rb
Class Model < ActiveRecord::Base
end
This is all you need - you'll be able to access domain.com/api/controller.json POST to create data, and domain.com/api/controller/4.json PUT to update it :)
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.
I want to send request to some third party service in xml and also expecting response in xml. I'm searching for some gem or any idea how to do this.
Thing which is in my mind is to
make some partail _example.xml.builder
onclick from my view to some button send ajax request to controller action and use render_to_string to render that xml doc and then
Save it in some variable
and then call to that service method in same action
But it is not proper thing as I expect there should be some thing more efficient than my suggested thing
RoR doesn't natively use XML so some degree of conversion is required.
Having said that, XML generation is very simple in RoR applications. There are several ways of doing this, my favourite being constructing the required data as a Hash (which is native to Ruby) then the_hash.to_xml.
The XML conversion can also be defined in a model Class if you wish a consistent result:
class Example < ActiveRecord::Base
# ensure that only column1, column2, etc are output as XML
def to_xml(options = {})
super( options.merge( select(:column1, :column2, etc) ) )
end
end
Then in your controller:
poster = Example.find(123)
request = Net::HTTP.new('www.example.com', 80)
request.post('/path', poster.to_xml)
Hopefully the above demonstrates a simple example of posting XML data to a remote host. As you mentioned, a more complicated XML can be constructed using xml.builder
HTH and good luck.
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