The default route file has the following segment:
# This is a legacy wild controller route that's not recommended for RESTful applications.
# Note: This route will make all actions in every controller accessible via GET requests.
# match ':controller(/:action(/:id))(.:format)'
So what exactly is the problem with letting Rails assume the controller, action, and view to display when the format is ':controller(/:action(/:id))(.:format)' ? I mean, for more specific things like nesting I can always use specific routes...
It's just a common-sense security measure.
It forces the designer to whitelist GET requests so that the app does not inadvertently divulge the contents of a table or record to a malicious user.
If developers are allowed to being lazy, they will be. We are super great at optimizing efforts.
Not being explicit about routes may help you save 5 seconds each time you have a new controller, but it will be a headache for new developers, which will be unfamiliar with your codebase and hence don't know how things are glued. They will have to reverse engineer your views-controllers.
Being there. Not pretty.
Being explicit is usually better.
Because not everyone is building the next Facebook and some of us do this for fun or to make simple apps that we use only on our development device or to simply not be a ass and "answer" a question with that actually has an answer, instead of make ourselves look uber smart and important:
match '/:controller(/:action(/:id))(.:format)', to: "#{:controller}#{:action}", via: [:get, :post]
Related
I am writing my first "real" Rails app after making a few successful learning projects and I'm still a little bit iffy on best practises for App vs API routes. Ideally, API routes will be RESTful, mainly revolving around using resources :articles or whatever, but I'm wondering which of the the following two scenarios is best
Create the app itself using RESTful routes, then bolt on an API in the "api" namespace/subdomain for example, and after authentication, piggy back off the same routes (not sure exactly how to do the piggy backing yet with different authentication, but that will come later. Or;
Create the app itself with whatever routes are semantic and work well (still ideally CRUD based), then create an entirely new set of controllers and routes for the API (RESTful)
I appreciate any feedback that anyone has.
The default "Rails way" is to make your models available to different clients through a single set of URIs, handled by a single set of controllers — /articles for browsers, /articles.json or /articles.xml for programmatic access. You can see that in Rails' emphasis on "resources", the respond_to mechanism in your controllers and so on.
This often works very well for simpler APIs that match the CRUD semantics closely. If yours for whatever reason does not, you might be better off with separating it out.
Try starting out the simple way by sticking to this 'one controller for both' approach. If you like, you could use
scope '(api)' do
resources :articles
resources :authors
resources :comments
# ...
end
to add an optional '/api/' path segment to your URLs - in this case, giving you both /api/articles and /articles, routed to the same controller. This gives you an easy way out, should you later decide to switch to separate controllers for HTML and API access.
Before new Rails routing DSL was introduced in Rails 3, most applications sported simple, yet effective default routing rule that fit 99% of scenarios and in fact made it possible to do tons of stuff without even thinking of touching routing config:
# Install the default route as the lowest priority.
map.connect ':controller/:action/:id.:format'
map.connect ':controller/:action/:id'
If I understand correctly, since introducing of new routing DSL in Rails 3, this practice is deprecated. I know that this behavior can be sort of emulated using dynamic segments definition, but anyway, there's no "default route of lowest priority" generated anymore.
Why is it so? Any rationale? Current documentation or RailsCasts explain syntax in detail, but they really don't give any information on why it is considered a bad, outdated practice to use catch-all lowest priority default routing rule?
You should not use map.connect in Rails 2 just as you should not use dynamic segments and match in Rails 3. Catch-all routing is vulnerable to CSRF attacks. You'll probably learn a lot more from researching this than me trying to explaining it to you but basically:
POST requests are the only types of requests that should be able to change the state of an application. So you usually change state through HTML form submissions.
Using catch-all routing, both GET and POST requests get redirected to the same actions. This means that you can change state using GET requests!
This is incredibly dangerous. Let's imagine that some bank uses catch-all routing. I can forge a url like this:
http://somebank.com/withdraw?amount=1000000&from=GreyCat&to=Ashitaka
And send you the link. By accessing it, you would change the application state without submitting an HTML form through a POST request.
This is why, in Rails 4, match was fixed to only work with the via option, like so:
match "/users/:id" => "users#show", :via => :get
People were using match willy-nilly without taking into consideration the problems they were creating by using it. So now match needs to be written like that. However, you could (and should) rewrite it as:
get "/users/:id" => "users#show"
I always like to link to our Russian Rails hacker security researcher Egor Homakov's blog because he explains the security problems he finds with lots of passion and fervour. And he actually contributes to Rails and tries to improve its security so he deserves all the recognition he can get. Here's his blog post explaining why match is evil.
I'm building a rails3 application and at the moment I have the following line in my routes.rb file:
get "/:id" => 'tapes#show'
In other words, you can show a Tape using website.com/tapes/1 and also by using website.com/1
(I am also using friendly_id gem so the user sees in fact a friendly URL in the form of website.com/tapename)
Now, what I would like to achieve is to do the same thing for Users pages. So instead of showing a User page using website.com/users/alex I want to be able to also use website.com/alex.
Is there a way to implement this 'users' logic in routes.rb together with the existing 'tapes' rule and somehow set a priority?
So if someone accesses website.com/alex my app would search if there is a User record with id 'alex' and if none is found then look for a Tape with id 'alex'.
Could I use some kind of Advanced Constraints in routes?
Any ideas?
Many thanks for the help,
Alex
Rails would have no way to determine which controller you were trying to access. The only way that this would be possible, is if either:
you could determine which model it would resolve to based upon some regular expression on the name.
or
You knew that user names and tape names never conflicted, and were willing to suffer the cost of hitting the database to resolve the correct controller.
This is probably a bad idea for a number of reasons, it would have performance implications, it also doesn't conform to RESTful principles, and it would probably be confusing to users.
What are the cons in using the generic route:
match ':controller(/:action(/:id(.:format)))'
I was told it is not recommended yet I do not see why. What problems can I get from using this?
Because it's easier to implement REST if you use the opposite ordering, ':controller(/:id(/:action))', and rails now has more convenient ways to get the proper HTTP verb using explicit resource routes.
Understanding the basic principles of REST will make it easier for you to expose an API, should you choose to go down that route, which embraces the principles of HTTP. It also tends to keep you from doing certain unclever things, like making it possible to delete records using a GET request. (The uncleverness might not be discovered until Google or your internal search bot decides to index all the links to :delete actions.)
The basic idea is that a GET /Url should imply fetching a resource. When you put the action first, the resource is semi-obscured, and you're accidentally opening the door to potential errors because all of the HTTP methods can be used to call destructive actions. Using an HTTP verb-centric approach, you can send an UPDATE request to the same URL as the SHOW request.
A specific answer to your question, which I understand to be understanding cons of the generic approach:
One significant risk is that you make every single controller action (non-protected) available to your users. It gives them the ability to access your entire 'tree' of controller actions, which may or may not be desirable depending on your situation.
In addition, you give users the ability to GET rather than POST, POST rather than GET, etc.
The comment above it that is generated explains it pretty nicely.
# Note: This route will make all actions in every controller accessible via GET requests.
This means that you could theoretically do a GET request on a route that is only supposed to be accessible via POST. ie. You could do add a route called /postable to a user object, which should only be POST'd to, but if you use the rule above you can also do a GET request on it (with empty parameters).
You don't really get problems, per se. Instead, you lose out on the advantages you gain from using resource routing:
A common pattern that helps simplify controller design
Free RESTlike handling of HTTP verbs (GET, PUT, POST, DELETE)
Peace, Love and Happiness*
You can find out more about resource routing by reading the Rails Routing Guide.
* Peace, Love and Happiness not available in all areas. Terms and conditions apply.
I've been messing about in rails the past 2 months and so far everything's going well - but there's one area I'm a little doubtful on.
I keep hearing about the joys of RESTful rails resources: that is, a 'resource :foo' in config/routes, and your 7 restful actions in the controller.
Except for very simple things (eg stuff that's 99% done by running 'generate scaffold'), I find it's less convenient to try squeeze my project functionality into that approach than to just match urls in config/routes one-by-one and do each action as needed.
But I keep getting the sense that I'm wrong, and that in all but the most extreme circumstances, RESTful resources are the way to go.
So:
(a) Can anyone offer an opinion on this?
(b) For experienced rails folks, what % of your routes in a typical project are :resources and what % are coded action-by-action?
Cheers...
Resources are convenient, but they are not a "one size fits all" feature. Some things just don't make sense with the 7 methods.
Keep in mind that you can:
Exclude specific methods with :except.
Include only specific methods with :only.
Add your own methods to the resource.
So they aren't as inflexible as you may think. But if, after taking those 3 points in mind, the resource just doesn't "feel right", skip it! REST was never meant to replace regular routing, it just tries to abstract away the most common use case.
If you skip out on RESTful resources completely, you'll be missing a ton of free functionality. Use it wisely and you'll be fine.
Generally I start a project with the REST architecture in mind. I build out my basic functionality this way, but as the project/website progresses I write more and more views that do not fit into the RESTful architecture. Marketing sites and parallel functionality are perfect examples of this.
Here is an article on the approach:
http://ablogaboutcode.com/2010/11/22/to-be-or-not-to-be-restful-ruby-on-rails-best-practices/
Before you get started, here are some questions you might want to ask yourself:
Does this controller/view deal primarily with an object/entity like a Post, Blog?
Are create, update, delete, edit and new actions all going to be available on the web?
As a guideline, if you answer YES to these two questions, then it’s probably best to start with REST and expect that you will eventually use the architecture as a building block for additional actions and views you might want to perform. Otherwise, pick a URL that best represents what the action will show or do (/archives, /tour, /december-offer) and make sure you use the proper HTTP Protocols (GET for display, PUT for update, DELETE for removing and POST for creating).