aboutsummaryrefslogtreecommitdiffstats
path: root/guides/source/routing.md
diff options
context:
space:
mode:
Diffstat (limited to 'guides/source/routing.md')
-rw-r--r--guides/source/routing.md233
1 files changed, 160 insertions, 73 deletions
diff --git a/guides/source/routing.md b/guides/source/routing.md
index 52f11f92bd..84de727c11 100644
--- a/guides/source/routing.md
+++ b/guides/source/routing.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Rails Routing from the Outside In
=================================
@@ -7,18 +7,18 @@ This guide covers the user-facing features of Rails routing.
After reading this guide, you will know:
-* How to interpret the code in `routes.rb`.
+* How to interpret the code in `config/routes.rb`.
* How to construct your own routes, using either the preferred resourceful style or the `match` method.
-* What parameters to expect an action to receive.
+* How to declare route parameters, which are passed onto controller actions.
* How to automatically create paths and URLs using route helpers.
-* Advanced techniques such as constraints and Rack endpoints.
+* Advanced techniques such as creating constraints and mounting Rack endpoints.
--------------------------------------------------------------------------------
The Purpose of the Rails Router
-------------------------------
-The Rails router recognizes URLs and dispatches them to a controller's action. It can also generate paths and URLs, avoiding the need to hardcode strings in your views.
+The Rails router recognizes URLs and dispatches them to a controller's action, or to a Rack application. It can also generate paths and URLs, avoiding the need to hardcode strings in your views.
### Connecting URLs to Code
@@ -36,6 +36,8 @@ get '/patients/:id', to: 'patients#show'
the request is dispatched to the `patients` controller's `show` action with `{ id: '17' }` in `params`.
+NOTE: Rails uses snake_case for controller names here, if you have a multiple word controller like `MonsterTrucksController`, you want to use `monster_trucks#show` for example.
+
### Generating Paths and URLs from Code
You can also generate paths and URLs. If the route above is modified to be:
@@ -47,7 +49,7 @@ get '/patients/:id', to: 'patients#show', as: 'patient'
and your application contains this code in the controller:
```ruby
-@patient = Patient.find(17)
+@patient = Patient.find(params[:id])
```
and this in the corresponding view:
@@ -58,6 +60,26 @@ and this in the corresponding view:
then the router will generate the path `/patients/17`. This reduces the brittleness of your view and makes your code easier to understand. Note that the id does not need to be specified in the route helper.
+### Configuring the Rails Router
+
+The routes for your application or engine live in the file `config/routes.rb` and typically looks like this:
+
+```ruby
+Rails.application.routes.draw do
+ resources :brands, only: [:index, :show] do
+ resources :products, only: [:index, :show]
+ end
+
+ resource :basket, only: [:show, :update, :destroy]
+
+ resolve("Basket") { route_for(:basket) }
+end
+```
+
+Since this is a regular Ruby source file you can use all of its features to help you define your routes but be careful with variable names as they can clash with the DSL methods of the router.
+
+NOTE: The `Rails.application.routes.draw do ... end` block that wraps your route definitions is required to establish the scope for the router DSL and must not be deleted.
+
Resource Routing: the Rails Default
-----------------------------------
@@ -79,11 +101,13 @@ it asks the router to map it to a controller action. If the first matching route
resources :photos
```
-Rails would dispatch that request to the `destroy` method on the `photos` controller with `{ id: '17' }` in `params`.
+Rails would dispatch that request to the `destroy` action on the `photos` controller with `{ id: '17' }` in `params`.
### CRUD, Verbs, and Actions
-In Rails, a resourceful route provides a mapping between HTTP verbs and URLs to controller actions. By convention, each action also maps to particular CRUD operations in a database. A single entry in the routing file, such as:
+In Rails, a resourceful route provides a mapping between HTTP verbs and URLs to
+controller actions. By convention, each action also maps to a specific CRUD
+operation in a database. A single entry in the routing file, such as:
```ruby
resources :photos
@@ -114,7 +138,7 @@ Creating a resourceful route will also expose a number of helpers to the control
* `edit_photo_path(:id)` returns `/photos/:id/edit` (for instance, `edit_photo_path(10)` returns `/photos/10/edit`)
* `photo_path(:id)` returns `/photos/:id` (for instance, `photo_path(10)` returns `/photos/10`)
-Each of these helpers has a corresponding `_url` helper (such as `photos_url`) which returns the same path prefixed with the current host, port and path prefix.
+Each of these helpers has a corresponding `_url` helper (such as `photos_url`) which returns the same path prefixed with the current host, port, and path prefix.
### Defining Multiple Resources at the Same Time
@@ -140,16 +164,17 @@ Sometimes, you have a resource that clients always look up without referencing a
get 'profile', to: 'users#show'
```
-Passing a `String` to `get` will expect a `controller#action` format, while passing a `Symbol` will map directly to an action:
+Passing a `String` to `to:` will expect a `controller#action` format. When using a `Symbol`, the `to:` option should be replaced with `action:`. When using a `String` without a `#`, the `to:` option should be replaced with `controller:`:
```ruby
-get 'profile', to: :show
+get 'profile', action: :show, controller: 'users'
```
This resourceful route:
```ruby
resource :geocoder
+resolve('Geocoder') { [:geocoder] }
```
creates six different routes in your application, all mapping to the `Geocoders` controller:
@@ -171,15 +196,7 @@ A singular resourceful route generates these helpers:
* `edit_geocoder_path` returns `/geocoder/edit`
* `geocoder_path` returns `/geocoder`
-As with plural resources, the same helpers ending in `_url` will also include the host, port and path prefix.
-
-WARNING: A [long-standing bug](https://github.com/rails/rails/issues/1769) prevents `form_for` from working automatically with singular resources. As a workaround, specify the URL for the form directly, like so:
-
-```ruby
-form_for @geocoder, url: geocoder_path do |f|
-
-# snippet for brevity
-```
+As with plural resources, the same helpers ending in `_url` will also include the host, port, and path prefix.
### Controller Namespaces and Routing
@@ -250,11 +267,11 @@ TIP: _If you need to use a different controller namespace inside a `namespace` b
It's common to have resources that are logically children of other resources. For example, suppose your application includes these models:
```ruby
-class Magazine < ActiveRecord::Base
+class Magazine < ApplicationRecord
has_many :ads
end
-class Ad < ActiveRecord::Base
+class Ad < ApplicationRecord
belongs_to :magazine
end
```
@@ -390,7 +407,7 @@ The comments resource here will have the following routes generated for it:
### Routing concerns
-Routing Concerns allows you to declare common routes that can be reused inside other resources and routes. To define a concern:
+Routing concerns allow you to declare common routes that can be reused inside other resources and routes. To define a concern:
```ruby
concern :commentable do
@@ -423,7 +440,7 @@ resources :articles do
end
```
-Also you can use them in any place that you want inside the routes, for example in a scope or namespace call:
+Also you can use them in any place that you want inside the routes, for example in a `scope` or `namespace` call:
```ruby
namespace :articles do
@@ -543,7 +560,7 @@ TIP: If you find yourself adding many extra actions to a resourceful route, it's
Non-Resourceful Routes
----------------------
-In addition to resource routing, Rails has powerful support for routing arbitrary URLs to actions. Here, you don't get groups of routes automatically generated by resourceful routing. Instead, you set up each route within your application separately.
+In addition to resource routing, Rails has powerful support for routing arbitrary URLs to actions. Here, you don't get groups of routes automatically generated by resourceful routing. Instead, you set up each route separately within your application.
While you should usually use resourceful routing, there are still many places where the simpler routing is more appropriate. There's no need to try to shoehorn every last piece of your application into a resourceful framework if that's not a good fit.
@@ -551,29 +568,23 @@ In particular, simple routing makes it very easy to map legacy URLs to new Rails
### Bound Parameters
-When you set up a regular route, you supply a series of symbols that Rails maps to parts of an incoming HTTP request. Two of these symbols are special: `:controller` maps to the name of a controller in your application, and `:action` maps to the name of an action within that controller. For example, consider this route:
+When you set up a regular route, you supply a series of symbols that Rails maps to parts of an incoming HTTP request. For example, consider this route:
```ruby
-get ':controller(/:action(/:id))'
+get 'photos(/:id)', to: 'photos#display'
```
-If an incoming request of `/photos/show/1` is processed by this route (because it hasn't matched any previous route in the file), then the result will be to invoke the `show` action of the `PhotosController`, and to make the final parameter `"1"` available as `params[:id]`. This route will also route the incoming request of `/photos` to `PhotosController#index`, since `:action` and `:id` are optional parameters, denoted by parentheses.
+If an incoming request of `/photos/1` is processed by this route (because it hasn't matched any previous route in the file), then the result will be to invoke the `display` action of the `PhotosController`, and to make the final parameter `"1"` available as `params[:id]`. This route will also route the incoming request of `/photos` to `PhotosController#display`, since `:id` is an optional parameter, denoted by parentheses.
### Dynamic Segments
-You can set up as many dynamic segments within a regular route as you like. Anything other than `:controller` or `:action` will be available to the action as part of `params`. If you set up this route:
+You can set up as many dynamic segments within a regular route as you like. Any segment will be available to the action as part of `params`. If you set up this route:
```ruby
-get ':controller/:action/:id/:user_id'
+get 'photos/:id/:user_id', to: 'photos#show'
```
-An incoming path of `/photos/show/1/2` will be dispatched to the `show` action of the `PhotosController`. `params[:id]` will be `"1"`, and `params[:user_id]` will be `"2"`.
-
-NOTE: You can't use `:namespace` or `:module` with a `:controller` path segment. If you need to do this then use a constraint on :controller that matches the namespace you require. e.g:
-
-```ruby
-get ':controller(/:action(/:id))', controller: /admin\/[^\/]+/
-```
+An incoming path of `/photos/1/2` will be dispatched to the `show` action of the `PhotosController`. `params[:id]` will be `"1"`, and `params[:user_id]` will be `"2"`.
TIP: By default, dynamic segments don't accept dots - this is because the dot is used as a separator for formatted routes. If you need to use a dot within a dynamic segment, add a constraint that overrides this – for example, `id: /[^\/]+/` allows anything except a slash.
@@ -582,38 +593,40 @@ TIP: By default, dynamic segments don't accept dots - this is because the dot is
You can specify static segments when creating a route by not prepending a colon to a fragment:
```ruby
-get ':controller/:action/:id/with_user/:user_id'
+get 'photos/:id/with_user/:user_id', to: 'photos#show'
```
-This route would respond to paths such as `/photos/show/1/with_user/2`. In this case, `params` would be `{ controller: 'photos', action: 'show', id: '1', user_id: '2' }`.
+This route would respond to paths such as `/photos/1/with_user/2`. In this case, `params` would be `{ controller: 'photos', action: 'show', id: '1', user_id: '2' }`.
### The Query String
The `params` will also include any parameters from the query string. For example, with this route:
```ruby
-get ':controller/:action/:id'
+get 'photos/:id', to: 'photos#show'
```
-An incoming path of `/photos/show/1?user_id=2` will be dispatched to the `show` action of the `Photos` controller. `params` will be `{ controller: 'photos', action: 'show', id: '1', user_id: '2' }`.
+An incoming path of `/photos/1?user_id=2` will be dispatched to the `show` action of the `Photos` controller. `params` will be `{ controller: 'photos', action: 'show', id: '1', user_id: '2' }`.
### Defining Defaults
-You do not need to explicitly use the `:controller` and `:action` symbols within a route. You can supply them as defaults:
+You can define defaults in a route by supplying a hash for the `:defaults` option. This even applies to parameters that you do not specify as dynamic segments. For example:
```ruby
-get 'photos/:id', to: 'photos#show'
+get 'photos/:id', to: 'photos#show', defaults: { format: 'jpg' }
```
-With this route, Rails will match an incoming path of `/photos/12` to the `show` action of `PhotosController`.
+Rails would match `photos/12` to the `show` action of `PhotosController`, and set `params[:format]` to `"jpg"`.
-You can also define other defaults in a route by supplying a hash for the `:defaults` option. This even applies to parameters that you do not specify as dynamic segments. For example:
+You can also use `defaults` in a block format to define the defaults for multiple items:
```ruby
-get 'photos/:id', to: 'photos#show', defaults: { format: 'jpg' }
+defaults format: :json do
+ resources :photos
+end
```
-Rails would match `photos/12` to the `show` action of `PhotosController`, and set `params[:format]` to `"jpg"`.
+NOTE: You cannot override defaults via query parameters - this is for security reasons. The only defaults that can be overridden are dynamic segments via substitution in the URL path.
### Naming Routes
@@ -631,7 +644,7 @@ You can also use this to override routing methods defined by resources, like thi
get ':username', to: 'users#show', as: :user
```
-This will define a `user_path` method that will be available in controllers, helpers and views that will go to a route such as `/bob`. Inside the `show` action of `UsersController`, `params[:username]` will contain the username for the user. Change `:username` in the route definition if you do not want your parameter name to be `:username`.
+This will define a `user_path` method that will be available in controllers, helpers, and views that will go to a route such as `/bob`. Inside the `show` action of `UsersController`, `params[:username]` will contain the username for the user. Change `:username` in the route definition if you do not want your parameter name to be `:username`.
### HTTP Verb Constraints
@@ -649,7 +662,7 @@ match 'photos', to: 'photos#show', via: :all
NOTE: Routing both `GET` and `POST` requests to a single action has security implications. In general, you should avoid routing all verbs to an action unless you have a good reason to.
-NOTE: 'GET' in Rails won't check for CSRF token. You should never write to the database from 'GET' requests, for more information see the [security guide](security.html#csrf-countermeasures) on CSRF countermeasures.
+NOTE: `GET` in Rails won't check for CSRF token. You should never write to the database from `GET` requests, for more information see the [security guide](security.html#csrf-countermeasures) on CSRF countermeasures.
### Segment Constraints
@@ -702,14 +715,16 @@ end
NOTE: Request constraints work by calling a method on the [Request object](action_controller_overview.html#the-request-object) with the same name as the hash key and then compare the return value with the hash value. Therefore, constraint values should match the corresponding Request object method return type. For example: `constraints: { subdomain: 'api' }` will match an `api` subdomain as expected, however using a symbol `constraints: { subdomain: :api }` will not, because `request.subdomain` returns `'api'` as a String.
+NOTE: There is an exception for the `format` constraint: while it's a method on the Request object, it's also an implicit optional parameter on every path. Segment constraints take precedence and the `format` constraint is only applied as such when enforced through a hash. For example, `get 'foo', constraints: { format: 'json' }` will match `GET /foo` because the format is optional by default. However, you can [use a lambda](#advanced-constraints) like in `get 'foo', constraints: lambda { |req| req.format == :json }` and the route will only match explicit JSON requests.
+
### Advanced Constraints
-If you have a more advanced constraint, you can provide an object that responds to `matches?` that Rails should use. Let's say you wanted to route all users on a blacklist to the `BlacklistController`. You could do:
+If you have a more advanced constraint, you can provide an object that responds to `matches?` that Rails should use. Let's say you wanted to route all users on a restricted list to the `RestrictedListController`. You could do:
```ruby
-class BlacklistConstraint
+class RestrictedListConstraint
def initialize
- @ips = Blacklist.retrieve_ips
+ @ips = RestrictedList.retrieve_ips
end
def matches?(request)
@@ -718,8 +733,8 @@ class BlacklistConstraint
end
Rails.application.routes.draw do
- get '*path', to: 'blacklist#index',
- constraints: BlacklistConstraint.new
+ get '*path', to: 'restricted_list#index',
+ constraints: RestrictedListConstraint.new
end
```
@@ -727,8 +742,8 @@ You can also specify constraints as a lambda:
```ruby
Rails.application.routes.draw do
- get '*path', to: 'blacklist#index',
- constraints: lambda { |request| Blacklist.retrieve_ips.include?(request.remote_ip) }
+ get '*path', to: 'restricted_list#index',
+ constraints: lambda { |request| RestrictedList.retrieve_ips.include?(request.remote_ip) }
end
```
@@ -793,7 +808,11 @@ get '/stories/:name', to: redirect { |path_params, req| "/articles/#{path_params
get '/stories', to: redirect { |path_params, req| "/articles/#{req.subdomain}" }
```
-Please note that this redirection is a 301 "Moved Permanently" redirect. Keep in mind that some web browsers or proxy servers will cache this type of redirect, making the old page inaccessible.
+Please note that default redirection is a 301 "Moved Permanently" redirect. Keep in mind that some web browsers or proxy servers will cache this type of redirect, making the old page inaccessible. You can use the `:status` option to change the response status:
+
+```ruby
+get '/stories/:name', to: redirect('/articles/%{name}', status: 302)
+```
In all of these cases, if you don't provide the leading host (`http://www.example.com`), Rails will take those details from the current request.
@@ -802,23 +821,23 @@ In all of these cases, if you don't provide the leading host (`http://www.exampl
Instead of a String like `'articles#index'`, which corresponds to the `index` action in the `ArticlesController`, you can specify any [Rack application](rails_on_rack.html) as the endpoint for a matcher:
```ruby
-match '/application.js', to: Sprockets, via: :all
+match '/application.js', to: MyRackApp, via: :all
```
-As long as `Sprockets` responds to `call` and returns a `[status, headers, body]`, the router won't know the difference between the Rack application and an action. This is an appropriate use of `via: :all`, as you will want to allow your Rack application to handle all verbs as it considers appropriate.
+As long as `MyRackApp` responds to `call` and returns a `[status, headers, body]`, the router won't know the difference between the Rack application and an action. This is an appropriate use of `via: :all`, as you will want to allow your Rack application to handle all verbs as it considers appropriate.
NOTE: For the curious, `'articles#index'` actually expands out to `ArticlesController.action(:index)`, which returns a valid Rack application.
If you specify a Rack application as the endpoint for a matcher, remember that
the route will be unchanged in the receiving application. With the following
-route your Rack application should expect the route to be '/admin':
+route your Rack application should expect the route to be `/admin`:
```ruby
match '/admin', to: AdminApp, via: :all
```
If you would prefer to have your Rack application receive requests at the root
-path instead, use mount:
+path instead, use `mount`:
```ruby
mount AdminApp, at: '/admin'
@@ -855,6 +874,49 @@ You can specify unicode character routes directly. For example:
get 'こんにちは', to: 'welcome#index'
```
+### Direct routes
+
+You can create custom URL helpers directly. For example:
+
+```ruby
+direct :homepage do
+ "http://www.rubyonrails.org"
+end
+
+# >> homepage_url
+# => "http://www.rubyonrails.org"
+```
+
+The return value of the block must be a valid argument for the `url_for` method. So, you can pass a valid string URL, Hash, Array, an Active Model instance, or an Active Model class.
+
+```ruby
+direct :commentable do |model|
+ [ model, anchor: model.dom_id ]
+end
+
+direct :main do
+ { controller: 'pages', action: 'index', subdomain: 'www' }
+end
+```
+
+### Using `resolve`
+
+The `resolve` method allows customizing polymorphic mapping of models. For example:
+
+``` ruby
+resource :basket
+
+resolve("Basket") { [:basket] }
+```
+
+``` erb
+<%= form_for @basket do |form| %>
+ <!-- basket form -->
+<% end %>
+```
+
+This will generate the singular URL `/basket` instead of the usual `/baskets/:id`.
+
Customizing Resourceful Routes
------------------------------
@@ -999,7 +1061,7 @@ scope ':username' do
end
```
-This will provide you with URLs such as `/bob/articles/1` and will allow you to reference the `username` part of the path as `params[:username]` in controllers, helpers and views.
+This will provide you with URLs such as `/bob/articles/1` and will allow you to reference the `username` part of the path as `params[:username]` in controllers, helpers, and views.
### Restricting the Routes Created
@@ -1077,16 +1139,30 @@ resources :videos, param: :identifier
```
```
- videos GET /videos(.:format) videos#index
- POST /videos(.:format) videos#create
- new_videos GET /videos/new(.:format) videos#new
-edit_videos GET /videos/:identifier/edit(.:format) videos#edit
+ videos GET /videos(.:format) videos#index
+ POST /videos(.:format) videos#create
+ new_video GET /videos/new(.:format) videos#new
+edit_video GET /videos/:identifier/edit(.:format) videos#edit
```
```ruby
Video.find_by(identifier: params[:identifier])
```
+You can override `ActiveRecord::Base#to_param` of a related model to construct
+a URL:
+
+```ruby
+class Video < ApplicationRecord
+ def to_param
+ identifier
+ end
+end
+
+video = Video.find_by(identifier: "Roman-Holiday")
+edit_video_path(video) # => "/videos/Roman-Holiday/edit"
+```
+
Inspecting and Testing Routes
-----------------------------
@@ -1094,16 +1170,16 @@ Rails offers facilities for inspecting and testing your routes.
### Listing Existing Routes
-To get a complete list of the available routes in your application, visit `http://localhost:3000/rails/info/routes` in your browser while your server is running in the **development** environment. You can also execute the `rake routes` command in your terminal to produce the same output.
+To get a complete list of the available routes in your application, visit `http://localhost:3000/rails/info/routes` in your browser while your server is running in the **development** environment. You can also execute the `rails routes` command in your terminal to produce the same output.
-Both methods will list all of your routes, in the same order that they appear in `routes.rb`. For each route, you'll see:
+Both methods will list all of your routes, in the same order that they appear in `config/routes.rb`. For each route, you'll see:
* The route name (if any)
* The HTTP verb used (if the route doesn't respond to all verbs)
* The URL pattern to match
* The routing parameters for the route
-For example, here's a small section of the `rake routes` output for a RESTful route:
+For example, here's a small section of the `rails routes` output for a RESTful route:
```
users GET /users(.:format) users#index
@@ -1112,13 +1188,24 @@ For example, here's a small section of the `rake routes` output for a RESTful ro
edit_user GET /users/:id/edit(.:format) users#edit
```
-You may restrict the listing to the routes that map to a particular controller setting the `CONTROLLER` environment variable:
+You can search through your routes with the grep option: -g. This outputs any routes that partially match the URL helper method name, the HTTP verb, or the URL path.
+
+```
+$ rails routes -g new_comment
+$ rails routes -g POST
+$ rails routes -g admin
+```
+
+If you only want to see the routes that map to a specific controller, there's the -c option.
-```bash
-$ CONTROLLER=users bin/rake routes
+```
+$ rails routes -c users
+$ rails routes -c admin/users
+$ rails routes -c Comments
+$ rails routes -c Articles::CommentsController
```
-TIP: You'll find that the output from `rake routes` is much more readable if you widen your terminal window until the output lines don't wrap.
+TIP: You'll find that the output from `rails routes` is much more readable if you widen your terminal window until the output lines don't wrap. You can also use --expanded option to turn on the expanded table formatting mode.
### Testing Routes