Rails Routing from the Outside In
2012-10-26 16:44
357 查看
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.1.1 Connecting URLs to Code
When your Rails application receives an incoming request1.2 Generating Paths and URLs from Code
You can also generate paths and URLs. If your application contains this code:2 Resource Routing: the Rails Default
Resource routing allows you to quickly declare all of the common routes for a given resourceful controller. Instead of declaring separate routes for your index, show, new, edit, create, updateand destroy actions, a resourceful route declares them in a single line of code.2.1 Resources on the Web
Browsers request pages from Rails by making a request for a URL using a specific HTTP method, such as GET, POST, PUT and DELETE. Each method is a request to perform an operation on the resource. A resource route maps a number of related requests to actions in a single controller.When your Rails application receives an incoming request for
2.2 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 asHTTP Verb | Path | action | used for |
---|---|---|---|
GET | /photos | index | display a list of all photos |
GET | /photos/new | new | return an HTML form for creating a new photo |
POST | /photos | create | create a new photo |
GET | /photos/:id | show | display a specific photo |
GET | /photos/:id/edit | edit | return an HTML form for editing a photo |
PUT | /photos/:id | update | update a specific photo |
DELETE | /photos/:id | destroy | delete a specific photo |
2.3 Paths and URLs
Creating a resourceful route will also expose a number of helpers to the controllers in your application. In the case of resources :photos:photos_path returns /photos
new_photo_path returns /photos/new
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.
Because the router uses the HTTP verb and URL to match inbound requests, four URLs map to seven different actions.
2.4 Defining Multiple Resources at the Same Time
If you need to create routes for more than one resource, you can save a bit of typing by defining them all with a single call to resources:2.5 Singular Resources
Sometimes, you have a resource that clients always look up without referencing an ID. For example, you would like /profile to always show the profile of the currently logged in user. In this case, you can use a singular resource to map /profile (rather than /profile/:id) to the show action.HTTP Verb | Path | action | used for |
---|---|---|---|
GET | /geocoder/new | new | return an HTML form for creating the geocoder |
POST | /geocoder | create | create the new geocoder |
GET | /geocoder | show | display the one and only geocoder resource |
GET | /geocoder/edit | edit | return an HTML form for editing the geocoder |
PUT | /geocoder | update | update the one and only geocoder resource |
DELETE | /geocoder | destroy | delete the geocoder resource |
A singular resourceful route generates these helpers:
new_geocoder_path returns /geocoder/new
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.
2.6 Controller Namespaces and Routing
You may wish to organize groups of controllers under a namespace. Most commonly, you might group a number of administrative controllers under an Admin:: namespace. You would place these controllers under the app/controllers/admin directory, and you can group them together in your router:HTTP Verb | Path | action | named helper |
---|---|---|---|
GET | /admin/posts | index | admin_posts_path |
GET | /admin/posts/new | new | new_admin_post_path |
POST | /admin/posts | create | admin_posts_path |
GET | /admin/posts/:id | show | admin_post_path(:id) |
GET | /admin/posts/:id/edit | edit | edit_admin_post_path(:id) |
PUT | /admin/posts/:id | update | admin_post_path(:id) |
DELETE | /admin/posts/:id | destroy | admin_post_path(:id) |
HTTP Verb | Path | action | named helper |
---|---|---|---|
GET | /admin/posts | index | posts_path |
GET | /admin/posts/new | new | new_post_path |
POST | /admin/posts | create | posts_path |
GET | /admin/posts/:id | show | post_path(:id) |
GET | /admin/posts/:id/edit | edit | edit_post_path(:id) |
PUT | /admin/posts/:id | update | post_path(:id) |
DELETE | /admin/posts/:id | destroy | post_path(:id) |
2.7 Nested Resources
It’s common to have resources that are logically children of other resources. For example, suppose your application includes these models:HTTPVerb | Path | action | used for |
---|---|---|---|
GET | /magazines/:magazine_id/ads | index | display a list of all ads for a specific magazine |
GET | /magazines/:magazine_id/ads/new | new | return an HTML form for creating a new ad belonging to a specific magazine |
POST | /magazines/:magazine_id/ads | create | create a new ad belonging to a specific magazine |
GET | /magazines/:magazine_id/ads/:id | show | display a specific ad belonging to a specific magazine |
GET | /magazines/:magazine_id/ads/:id/edit | edit | return an HTML form for editing an ad belonging to a specific magazine |
PUT | /magazines/:magazine_id/ads/:id | update | update a specific ad belonging to a specific magazine |
DELETE | /magazines/:magazine_id/ads/:id | destroy | delete a specific ad belonging to a specific magazine |
2.7.1 Limits to Nesting
You can nest resources within other nested resources if you like. For example:
/publishers/1/magazines/2/photos/3
The corresponding route helper would be publisher_magazine_photo_url, requiring you to specify objects at all three levels. Indeed, this situation is confusing enough that a popular article by Jamis Buck proposes a rule of thumb for good Rails design:
Resources should never be nested more than 1 level deep.
2.8 Creating Paths and URLs From Objects
In addition to using the routing helpers, Rails can also create paths and URLs from an array of parameters. For example, suppose you have this set of routes:2.9 Adding More RESTful Actions
You are not limited to the seven routes that RESTful routing creates by default. If you like, you may add additional routes that apply to the collection or individual members of the collection.2.9.1 Adding Member Routes
To add a member route, just add a member block into the resource block:
Within the block of member routes, each route name specifies the HTTP verb that it will recognize. You can use get, put, post, or delete here. If you don’t have multiple member routes, you can also pass :on to a route, eliminating the block:
To add a route to the collection:
Just as with member routes, you can pass :on to a route:
If you find yourself adding many extra actions to a resourceful route, it’s time to stop and ask yourself whether you’re disguising the presence of another resource.
3 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.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.
In particular, simple routing makes it very easy to map legacy URLs to new Rails actions.
3.1 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 one of the default Rails routes:3.2 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’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:
3.3 Static Segments
You can specify static segments when creating a route:3.4 The Query String
The params will also include any parameters from the query string. For example, with this route:3.5 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 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:
3.6 Naming Routes
You can specify a name for any route using the :as option.3.7 HTTP Verb Constraints
You can use the :via option to constrain the request to one or more HTTP methods:3.8 Segment Constraints
You can use the :constraints option to enforce a format for a dynamic segment:For example, the following routes would allow for posts with to_param values like 1-hello-worldthat always begin with a number and users with to_param values like david that never begin with a number to share the root namespace:
3.9 Request-Based Constraints
You can also constrain a route based on any method on the Request object that returns a String.You specify a request-based constraint the same way that you specify a segment constraint:
3.10 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 theBlacklistController. You could do:3.11 Route Globbing
Route globbing is a way to specify that a particular parameter should be matched to all the remaining parts of a route. For exampleWildcard segments can occur anywhere in a route. For example,
Technically a route can have even more than one wildcard segment. The matcher assigns segments to parameters in an intuitive way. For example,
Starting from Rails 3.1, wildcard routes will always match the optional format segment by default. For example if you have this route:
3.12 Redirection
You can redirect any path to another path using the redirect helper in your router: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.
3.13 Routing to Rack Applications
Instead of a String, like "posts#index", which corresponds to the index action in thePostsController, you can specify any Rack application as the endpoint for a matcher.For the curious, "posts#index" actually expands out toPostsController.action(:index), which returns a valid Rack application.
3.14 Using root
You can specify what Rails should route "/" to with the root method:4 Customizing Resourceful Routes
While the default routes and helpers generated by resources :posts will usually serve you well, you may want to customize them in some way. Rails allows you to customize virtually any generic part of the resourceful helpers.4.1 Specifying a Controller to Use
The :controller option lets you explicitly specify a controller to use for the resource. For example:HTTP Verb | Path | action | named helper |
---|---|---|---|
GET | /photos | index | photos_path |
GET | /photos/new | new | new_photo_path |
POST | /photos | create | photos_path |
GET | /photos/:id | show | photo_path(:id) |
GET | /photos/:id/edit | edit | edit_photo_path(:id) |
PUT | /photos/:id | update | photo_path(:id) |
DELETE | /photos/:id | destroy | photo_path(:id) |
4.2 Specifying Constraints
You can use the :constraints option to specify a required format on the implicit id. For example:You can specify a single constraint to apply to a number of routes by using the block form:
By default the :id parameter doesn’t accept dots – this is because the dot is used as a separator for formatted routes. If you need to use a dot within an :id add a constraint which overrides this – for example :id => /[^\/]+/ allows anything except a slash.
4.3 Overriding the Named Helpers
The :as option lets you override the normal naming for the named route helpers. For example:HTTP verb | Path | action | named helper |
---|---|---|---|
GET | /photos | index | images_path |
GET | /photos/new | new | new_image_path |
POST | /photos | create | images_path |
GET | /photos/:id | show | image_path(:id) |
GET | /photos/:id/edit | edit | edit_image_path(:id) |
PUT | /photos/:id | update | image_path(:id) |
DELETE | /photos/:id | destroy | image_path(:id) |
4.4 Overriding the new and edit Segments
The :path_names option lets you override the automatically-generated “new” and “edit” segments in paths:If you find yourself wanting to change this option uniformly for all of your routes, you can use a scope.
4.5 Prefixing the Named Route Helpers
You can use the :as option to prefix the named route helpers that Rails generates for a route. Use this option to prevent name collisions between routes using a path scope.To prefix a group of route helpers, use :as with scope:
The namespace scope will automatically add :as as well as :module and :path prefixes.
You can prefix routes with a named parameter also:
4.6 Restricting the Routes Created
By default, Rails creates routes for the seven default actions (index, show, new, create, edit, update, and destroy) for every RESTful route in your application. You can use the :only and :exceptoptions to fine-tune this behavior. The :only option tells Rails to create only the specified routes:The :except option specifies a route or list of routes that Rails should not create:
If your application has many RESTful routes, using :only and :except to generate only the routes that you actually need can cut down on memory use and speed up the routing process.
4.7 Translated Paths
Using scope, we can alter path names generated by resources:HTTP verb | Path | action | named helper |
---|---|---|---|
GET | /kategorien | index | categories_path |
GET | /kategorien/neu | new | new_category_path |
POST | /kategorien | create | categories_path |
GET | /kategorien/:id | show | category_path(:id) |
GET | /kategorien/:id/bearbeiten | edit | edit_category_path(:id) |
PUT | /kategorien/:id | update | category_path(:id) |
DELETE | /kategorien/:id | destroy | category_path(:id) |
4.8 Overriding the Singular Form
If you want to define the singular form of a resource, you should add additional rules to theInflector.4.9 Using :as in Nested Resources
The :as option overrides the automatically-generated name for the resource in nested route helpers. For example,5 Inspecting and Testing Routes
Rails offers facilities for inspecting and testing your routes.5.1 Seeing Existing Routes with rake
If you want a complete list of all of the available routes in your application, run rake routescommand. This will print all of your routes, in the same order that they appear in 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:
users GET /users(.:format) users#index POST /users(.:format) users#create new_user GET /users/new(.:format) users#new 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 CONTROLLERenvironment variable:
5.2 Testing Routes
Routes should be included in your testing strategy (just like the rest of your application). Rails offers three built-in assertions designed to make testing routes simpler:assert_generates
assert_recognizes
assert_routing
5.2.1 The assert_generates Assertion
assert_generates asserts that a particular set of options generate a particular path and can be used with default routes or custom routes.
assert_recognizes is the inverse of assert_generates. It asserts that a given path is recognized and routes it to a particular spot in your application.
The assert_routing assertion checks the route both ways: it tests that the path generates the options, and that the options generate the path. Thus, it combines the functions ofassert_generates and assert_recognizes.
相关文章推荐
- Simplest way to serve static data from outside the application server in a Java web application
- 由外之内的创建类(Construct classes from the outside in)
- 关于error:Cannot assign to 'self' outside of a method in the init family
- cannot assign to self outside of a method in the init family
- 14. 58. 5. 从table与model中获得单元格的值 Retrieve the value in cell (1,2) from the model
- Should I design my classes from the outside (interfaces first) or from the inside (data first)?
- Some of the continuous-integration tools that work with Ant (From Ant in Action 2nd Edition)
- cannot assign to 'self' outside of a method in the init family
- 关于error:Cannot assign to 'self' outside of a method in the init family
- 关于error:Cannot assign to 'self' outside of a method in the init family
- startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW flag
- 关于error:Cannot assign to 'self' outside of a method in the init family
- 174 Using the LIST command in Recovery Manager (RMAN), which two pieces of information from the RMAN
- Cannot assign to 'self' outside of a method in the init family
- Thread Safety in the Standard C++ Library(From MSDN)
- OpenGL ES From the Ground Up, Part 3: Viewports in Perspective
- No configuration found. Configuring ehcache from ehcache-failsafe.xml found in the classpath解决
- CAS TGT 校验不成功:No principal was found in the response from the CAS server.WHO: audit:unknown
- MySQL报错“ you can't specify the target table for ‘update’ in from clause”
- Could not load the "start.png" image referenced from a nib in the bundle with identifier