I recently worked on the web application Wishgram and its development quickly got more complex when the need for an API arose to support an iOS app.
I’m not a proponent for building controllers that handle both web browser requests and API calls, because it yields a less skinny controller with more complex code when web application and API specific code are mixed.
For example, responses to success and failure actions in web application requests are generally handled with
redirect_to, while API requests encapsulate the action outcome in a
XML response. In a RESTful controller with 6 actions, we are quickly losing the beach body of a skinny controller.
Usually, the login mechanisms for web application and API users differ, where the former typically uses a username or email with a password and the latter tends to use a certificate or an OAuth implementation for granting authentication. This was the case in my project, and handling two authentication methods in the controller added unnecessary complexity.
Furthermore, the flat structure of controllers — as produced by the Rails generators — became too cumbersome to organize with two or more separate areas of application functionality.
My solution and proposed Rails design pattern is to always group common controllers into modules and to have them inherit from a dedicated group parent controller. Thus, group specific code such as callbacks that would generally reside in the
ApplicationController can now be applied in isolation to a group in its dedicated parent controller. Even if, at the time of developing a Rails app, there are only plans for a basic web application this pattern should still be used to account for future growth.
Implementation of Controller Groups
In my example, we are creating a
Customers controller for the customer-facing web application, the company-internal back end system and the API.
1 2 3 4 5 6 7 8 9
After generating the controllers in the groups, we change the superclass from
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
Since all API controllers inherit from
ApiController, we can easily disable CSRF for API calls to make non-GET valid requests without affecting the behavior in the front and back end controllers. This is a great example to demonstrate the advantage of separate controller groups.
1 2 3 4
Now that we have the controllers separated, we want to separate the 3 different controller groups in individual subdomains thus reflecting a logical separation in the domain name. One bonus with this approach is that cookies won’t be shared between our front and back end.
constraints option is used to tie controller groups to subdomains, and the
scope specifies the
Module that denotes a controller group without exposing it in the route path.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
At this point, we also want to future-proof the API and allow for supplemental versions. Ryan Bates’ REST API Versioning Rail Cast #350 provides a perfect solution. We will integrate it by creating a customized constraint for the API version route
1 2 3 4 5 6 7 8 9 10 11
At this time, there is no functionality in the Rails generator to set the inheritance for a controller. I’m currently looking into building a gem to add such a feature to facilitate the use of the proposed Rails design pattern.
The example application code used in this blog post is on Github, so feel free to fork it, and please share your thoughts on the proposed Rails pattern.