- Modern Web Development with ASP.NET Core 3
- Ricardo Peres
- 804字
- 2021-06-18 18:35:59
Applying route constraints
When we define a route template or pattern, we may also want to specify how that route shall be matched, which is constraining it. We can constrain a route in a number of ways, such as these:
- The request needs to match a given HTTP method.
- The request needs to match a given content type.
- Its parameters need to match certain rules.
A constraint can be expressed in the route template or as a discrete object, using the MapControllerRoute method. If you choose to use the route template, you need to specify its name next to the token to which it applies:
{controller=Home}/{action=Index}/{id:int}
Notice {id:int}: this constrains the id parameter to an integer, and is one of the provided constraints that we will talk about in a moment. Another option is to make use of the defaults parameter:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" },
constraints: new { id = new IntRouteConstraint() });
});
You should be able to guess that the anonymous class that is passed in the constraints parameter must have properties that match the route parameters.
Following on from this example, you can also pass constraints that are not bound to any route parameter, but instead perform some kind of bespoke validation, as follows:
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" },
constraints: new { foo = new BarRouteConstraint() });
In this case, the BarRouteConstraintconstraint class will still be called and can be used to invalidate a route selection.
HTTP methods
As we said earlier, in order to make an action method available to only some HTTP verbs or a specific content type, you can use one of the following:
- HttpGetAttribute
- HttpPostAttribute
- HttpPutAttribute
- HttpDeleteAttribute
- HttpOptionsAttribute
- HttpPatchAttribute
- HttpHeadAttribute
- ActionVerbsAttribute
- ConsumesAttribute
The names should be self-explanatory. You can add attributes for different verbs, and if any of them is present, the route will only match if its verb matches one of these attributes. ActionVerbsAttribute lets you pass a single method, or a list of methods, that you wish to support. ConsumesAttribute takes a valid content type.
Default constraints
ASP.NET Core includes the following constraints:

A route parameter can take many constraints at once, separated by :, as here:
Calculator/Calculate({a:int:max(10)},{b:int:max(10)})
In this example, the a and bparameters need to be integers and have a maximum value of 10, at the same time. Another example follows:
Book/Find({isbn:regex(^d{9}[d|X]$)])
This will match an ISBN string starting with nine digits and followed by either a trailing digit or the X character.
It is also possible to provide your own custom constraints, which we will see next.
Creating custom constraints
A constraint is any class that implements IRouteConstraint. If it is meant to be used inline in a route template, then it must be registered. Here's an example of a route constraint for validating even numbers:
public class EvenIntRouteConstraint : IRouteConstraint
{
public bool Match(
HttpContext httpContext,
IRouter route,
string routeKey,
RouteValueDictionary values,
RouteDirection routeDirection)
{
if ((!values.ContainsKey(routeKey)) || (values[routeKey] == null))
{
return false;
}
var value = values[routeKey].ToString();
if (!int.TryParse(value, out var intValue))
{
return false;
}
return (intValue % 2) == 0;
}
}
You should be able to tell that all route parameters are provided in the values collection and that the route parameter name is in routeKey. If no route parameter is actually supplied, it will just return false, as it will if the parameter cannot be parsed into an integer. Now, to register your constraint, you need to use the AddRouting method shown earlier this chapter:
services.AddRouting(options =>
{
options.ConstraintMap.Add("evenint", typeof(EvenIntRouteConstraint));
});
This is actually the same as retrieving RouteOptions from the registered configuration:
services.Configure<RouteOptions>(options => {
//do the same });
That's all there is to it.
If you wish to use a route constraint to validate a URL—or any of the request parameters—you can use a route constraint not bound to a route key:
public class IsAuthenticatedRouteConstraint : IRouteConstraint
{
public bool Match(
HttpContext httpContext,
IRouter route,
string routeKey,
RouteValueDictionary values,
RouteDirection routeDirection)
{
return httpContext.Request.Cookies.ContainsKey("auth");
}
}
Granted, there are other (even better) ways to do this; this was only included as an example.
Now we can use it like this, in a route:
Calculator/Calculate({a:evenint},{b:evenint})
If, on the other hand, you prefer to use the constraint classes directly in your MapControllerRoute calls, you do not need to register them. Regardless, the route constraint collection is available as the IInlineConstraintResolver service:
var inlineConstraintResolver = routes
.ServiceProvider
.GetRequiredService<IInlineConstraintResolver>();
In this chapter, we've seen how to define constraints for route tokens, including creating our own, which can be very useful for validating URLs upfront. The next section explains what data tokens are.
- Facebook Application Development with Graph API Cookbook
- TypeScript入門與實戰
- 高級C/C++編譯技術(典藏版)
- Python機器學習之金融風險管理
- Scala編程(第5版)
- 深入分析GCC
- R的極客理想:量化投資篇
- Building a Media Center with Raspberry Pi
- Java程序設計及應用開發
- React.js實戰
- Scala編程(第4版)
- 面向物聯網的Android應用開發與實踐
- Getting Started with Backbone Marionette
- Learning Java by Building Android Games
- LabVIEW案例實戰