Skip to content

Commit

Permalink
Improved routing info on regex constraints, plus more (#2450)
Browse files Browse the repository at this point in the history
Also fixed some broken/inaccurate links, typos, etc.
  • Loading branch information
Eilon authored and Rick-Anderson committed Jan 3, 2017
1 parent 1a85bf2 commit 3a0f4e6
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 24 deletions.
45 changes: 26 additions & 19 deletions aspnetcore/fundamentals/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,29 +310,36 @@ Route constraints execute when a `Route` has matched the syntax of the incoming
The following table demonstrates some route constraints and their expected behavior.

| constraint | Example | Example Match | Notes |
| constraint | Example | Example Matches | Notes |
| -------- | ------- | ------------- | ----- |
| `int` | {id:int} | 123 | Matches any integer |
| `bool` | {active:bool} | true | Matches `true` or `false` |
| `datetime` | {dob:datetime} | 2016-01-01 | Matches a valid `DateTime` value (in the invariant culture - see options) |
| `decimal` | {price:decimal} | 49.99 | Matches a valid `decimal` value |
| `double` | {weight:double} | 4.234 | Matches a valid `double` value |
| `float` | {weight:float} | 3.14 | Matches a valid `float` value |
| `guid` | {id:guid} | CD2C1638-1638-72D5-1638-DEADBEEF1638 | Matches a valid `Guid` value |
| `long` | {ticks:long} | 123456789 | Matches a valid `long` value |
| `minlength(value)` | {username:minlength(4)} | Rick | String must be at least 4 characters |
| `maxlength(value)` | {filename:maxlength(8)} | somefile | String must be no more than 8 characters |
| `length(min,max)` | {filename:length(8,16)} | *Somefile.txt* | String must be at least 8 and no more than 16 characters long |
| `min(value)` | {age:min(18)} | 19 | Value must be at least 18 |
| `max(value)` | {age:max(120)} | 91 | Value must be no more than 120 |
| `range(min,max)` | {age:range(18,120)} | 91 | Value must be at least 18 but no more than 120 |
| `alpha` | {name:alpha} | Rick | String must consist of alphabetical characters |
| `regex(expression)` | {ssn:regex(^d{3}-d{2}-d{4}$)} | 123-45-6789 | String must match the regular expression |
| `required` | {name:required} | Rick | Used to enforce that a non-parameter value is present during URL generation |
| `int` | `{id:int}` | `123456789`, `-123456789` | Matches any integer |
| `bool` | `{active:bool}` | `true`, `FALSE` | Matches `true` or `false` (case-insensitive) |
| `datetime` | `{dob:datetime}` | `2016-12-31`, `2016-12-31 7:32pm` | Matches a valid `DateTime` value (in the invariant culture - see warning) |
| `decimal` | `{price:decimal}` | `49.99`, `-1,000.01` | Matches a valid `decimal` value (in the invariant culture - see warning) |
| `double` | `{weight:double}` | `1.234`, `-1,001.01e8` | Matches a valid `double` value (in the invariant culture - see warning) |
| `float` | `{weight:float}` | `1.234`, `-1,001.01e8` | Matches a valid `float` value (in the invariant culture - see warning) |
| `guid` | `{id:guid}` | `CD2C1638-1638-72D5-1638-DEADBEEF1638`, `{CD2C1638-1638-72D5-1638-DEADBEEF1638}` | Matches a valid `Guid` value |
| `long` | `{ticks:long}` | `123456789`, `-123456789` | Matches a valid `long` value |
| `minlength(value)` | `{username:minlength(4)}` | `Rick` | String must be at least 4 characters |
| `maxlength(value)` | `{filename:maxlength(8)}` | `Richard` | String must be no more than 8 characters |
| `length(length)` | `{filename:length(12)}` | `somefile.txt` | String must be exactly 12 characters long |
| `length(min,max)` | `{filename:length(8,16)}` | `somefile.txt` | String must be at least 8 and no more than 16 characters long |
| `min(value)` | `{age:min(18)}` | `19` | Integer value must be at least 18 |
| `max(value)` | `{age:max(120)}` | `91` | Integer value must be no more than 120 |
| `range(min,max)` | `{age:range(18,120)}` | `91` | Integer value must be at least 18 but no more than 120 |
| `alpha` | `{name:alpha}` | `Rick` | String must consist of one or more alphabetical characters (`a`-`z`, case-insensitive) |
| `regex(expression)` | `{ssn:regex(^\\d{{3}}-\\d{{2}}-\\d{{4}}$)}` | `123-45-6789` | String must match the regular expression (see tips about defining a regular expression) |
| `required` | `{name:required}` | `Rick` | Used to enforce that a non-parameter value is present during URL generation |

>[!WARNING]
> Route constraints that verify the URL can be converted to a CLR type (such as `int` or `DateTime`) always use the invariant culture - they assume the URL is non-localizable. The framework-provided route constraints do not modify the values stored in route values. All route values parsed from the URL will be stored as strings. For example, the [Float route constraint](https://github.com/aspnet/Routing/blob/1.0.0/src/Microsoft.AspNetCore.Routing/Constraints/FloatRouteConstraint.cs#L44-L60) will attempt to convert the route value to a float, but the converted value is used only to verify it can be converted to a float.
>[!TIP]
> Regular expressions use delimiters and tokens similar to those used by Routing and the C# language, and so those tokens must be escaped. For example, to use the regular expression `^\d{3}-\d{2}-\d{4}$` in Routing, it needs to have the `\` characters typed in as `\\` in the C# source file to escape the `\` string escape character (unless using [verbatim string literals](https://msdn.microsoft.com/en-us/library/aa691090(v=vs.71).aspx)). The `{` and `}` characters need to be escaped by doubling them to escape the Routing parameter delimiter characters. The resulting regular expression is `^\\d{{3}}-\\d{{2}}-\\d{{4}}$`.
>[!TIP]
> Regular expressions used in routing will often start with the `^` character (match starting position of the string) and end with the `$` character (match ending position of the string) to ensure that the regular expression must match the entire route parameter value. Without the `^` and `$` characters the regular expression will match any sub-string within the string, which is often not desirable. For example, the regular expression `[a-z]{2}` will match the strings `hello` and `123abc456` because each contains a sequence of two lowercase characters somewhere in the string. However, the regular expression `^[a-z]{2}$` will match neither `hello` or `123abc456` because it matches only exactly a sequence of two lowercase characters, for example, `MZ`. Refer to [.NET Framework Regular Expressions](https://msdn.microsoft.com/en-us/library/hs600312(v=vs.110).aspx) for more information on regular expression syntax.
>[!TIP]
> To constrain a parameter to a known set of possible values, you can use a regular expression ( for example `{action:regex(list|get|create)}`. This would only match the `action` route value to `list`, `get`, or `create`. If passed into the constraints dictionary, the string "list|get|create" would be equivalent. Constraints that are passed in the constraints dictionary (not inline within a template) that don't match one of the known constraints are also treated as regular expressions.
Expand Down Expand Up @@ -366,4 +373,4 @@ routes.MapRoute("blog_route", "blog/{*slug}",
defaults: new { controller = "Blog", action = "ReadPost" });
```

Link generation would only generate a link for this route when the matching values for controller and action are provided.
Link generation would only generate a link for this route when the matching values for controller and action are provided.
31 changes: 26 additions & 5 deletions aspnetcore/mvc/controllers/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ The route template:

* `{id?}` defines `id` as optional

Default and optional route parameters do not need to be present in the URL path for a match. See the [Routing](../../fundamentals/routing.md) for a detailed description of route template syntax.
Default and optional route parameters do not need to be present in the URL path for a match. See [Route Template Reference](../../fundamentals/routing.md#route-template-reference) for a detailed description of route template syntax.

`"{controller=Home}/{action=Index}/{id?}"` can match the URL path `/` and will produce the route values `{ controller = Home, action = Index }`. The values for `controller` and `action` make use of the default values, `id` does not produce a value since there is no corresponding segment in the URL path. MVC would use these route values to select the `HomeController` and `Index` action:

Expand Down Expand Up @@ -123,7 +123,7 @@ routes.DefaultHandler = new MvcRouteHandler(...);
app.UseRouter(routes.Build());
```

`UseMvc` does not directly define any routes, it adds a placeholder to the route collection for the `attribute` route. The overload `UseMvc(Action<IRouteBuilder>)` lets you add your own routes and also supports attribute routing. `UseMvc` and all of its variations adds a placeholder for the attribute route - attribute routing is always available regardless of how you configure `UseMvc`. `UseMvcWithDefaultRoute` defines a default route and supports attribute routing. The <!-- Some exception as occured. Possible loss of data --> section includes more details on attribute routing.
`UseMvc` does not directly define any routes, it adds a placeholder to the route collection for the `attribute` route. The overload `UseMvc(Action<IRouteBuilder>)` lets you add your own routes and also supports attribute routing. `UseMvc` and all of its variations adds a placeholder for the attribute route - attribute routing is always available regardless of how you configure `UseMvc`. `UseMvcWithDefaultRoute` defines a default route and supports attribute routing. The [Attribute Routing](#attribute-routing-ref-label) section includes more details on attribute routing.

<a name=routing-conventional-ref-label></a>

Expand Down Expand Up @@ -288,9 +288,9 @@ public class MyDemoController : Controller
```

> [!NOTE]
> The route templates above doesn't define route parameters for `action`, `area`, and `controller`. In fact, these route parameters are not allowed in attribute routes. Since the route template is already assocated with an action, it wouldn't make sense to parse the action name from the URL.
> The route templates above don't define route parameters for `action`, `area`, and `controller`. In fact, these route parameters are not allowed in attribute routes. Since the route template is already assocated with an action, it wouldn't make sense to parse the action name from the URL.
Attribute routing can also make use of the `HTTP[Verb]` attributes such as `HttpPostAttribute`. All of these attributes can accept a route template. This example shows two actions that match the same route template:
Attribute routing can also make use of the `Http[Verb]` attributes such as `HttpPostAttribute`. All of these attributes can accept a route template. This example shows two actions that match the same route template:

<!-- literal_block {"ids": [], "names": [], "highlight_args": {}, "backrefs": [], "dupnames": [], "linenos": false, "classes": [], "xml:space": "preserve", "language": "c#"} -->

Expand All @@ -308,7 +308,7 @@ public IActionResult CreateProduct(...)
}
```

For a URL path like `/products` the `ProductsApi.ListProducts` action will be executed when the HTTP verb is `GET` and `ProductsApi.CreateProduct` will be executed when the HTTP verb is `POST`. Attribute routing first matches the URL against the set of route templates defined by route attributes. Once a route template matches, `IActionConstraint` constraints are applied to determine which actions can be executed.
For a URL path like `/products` the `ProductsApi.ListProducts` action will be executed when the HTTP verb is `GET` and `ProductsApi.CreateProduct` will be executed when the HTTP verb is `POST`. Attribute routing first matches the URL against the set of route templates defined by route attributes. Once a route template matches, `IActionConstraint` constraints are applied to determine which actions can be executed.

> [!TIP]
> When building a REST API, it's rare that you will want to use `[Route(...)]` on an action method. It's better to use the more specific `Http*Verb*Attributes` to be precise about what your API supports. Clients of REST APIs are expected to know what paths and HTTP verbs map to specific logical operations.
Expand Down Expand Up @@ -428,6 +428,8 @@ public class ProductsController : MyBaseController

Token replacement also applies to route names defined by attribute routes. `[Route("[controller]/[action]", Name="[controller]_[action]")]` will generate a unique route name for each action.

To match the literal token replacement delimiter `[` or `]`, escape it by repeating the character (`[[` or `]]`).

<a name=routing-multiple-routes-ref-label></a>

### Multiple Routes
Expand Down Expand Up @@ -478,6 +480,25 @@ public class ProductsController : Controller
> [!TIP]
> While using multiple routes on actions can seem powerful, it's better to keep your application's URL space simple and well-defined. Use multiple routes on actions only where needed, for example to support existing clients.


<a name=routing-attr-options></a>

### Specifying attribute route optional parameters, default values, and constraints

Attribute routes support the same inline syntax as conventional routes to specify optional parameters, default values, and constraints.

<!-- literal_block {"ids": [], "names": [], "highlight_args": {}, "backrefs": [], "dupnames": [], "linenos": false, "classes": [], "xml:space": "preserve", "language": "c#"} -->

```csharp
[HttpPost("product/{id:int}")]
public IActionResult ShowProduct(int id)
{
// ...
}
```

See [Route Template Reference](../../fundamentals/routing.md#route-template-reference) for a detailed description of route template syntax.

<a name=routing-cust-rt-attr-irt-ref-label></a>

### Custom route attributes using `IRouteTemplateProvider`
Expand Down

0 comments on commit 3a0f4e6

Please sign in to comment.