Wednesday, October 1, 2014

Constraints in Attribute Based Routing MVC5

In the previous article we have discussed about the attribute based Routing where the Routes can be specified directly above the Action method or Controller class itself. Now going ahead we will discuss about the Constrained that can be used in the Route.

Constraints are nothing but the validation which we can directly apply on the action methods using the Attribute based routing. With this you also gain the ability to validate your actions more like function overrides, a route will only be matched if the data type matches or the validation we have applied otherwise the request will fall through to the next route matching the pattern, looking for a supported data type.

Some of the common and widely used Constraints are as follows.

Data Type Related

[Route("MyRoute/{x:int}")]  // Matches 32 Bit integer value
public User GetUserById(int id) { ... }
 

[Route("MyRoute/{x:long}")]  // Matches 64 Bit integer value
public User GetUserById(long id) { ... }


[Route("MyRoute/{x:float}")]  // Matches 32 Bit floating point value
public User GetUserById(float id) { ... }


 [Route("MyRoute/{x:double}")]  // Matches 64 Bit floating point value
public User GetUserById(double id) { ... }

[Route("MyRoute/{x:decimal}")]  // Matches a decimal value
public User GetUserById(decimal id) { ... }


[Route("MyRoute/{x:bool}")]  // Matches a boolean value
public User GetUserByCondition(bool id) { ... }


[Route("MyRoute/{x:guid}")] // Matches a Unique Identifier value
public User GetUserById(GUID id) { ... }
 

[Route("MyRoute/{x:datetime}")]  // Matches a datetime value
public User GetUserByIdLoginTime(datetime id) { ... }
 

String Related Constraints 

[Route("MyRoute/{x:alpha}")]  // Matches upper and lower case value from a-z
public User GetUserByName(string id) { ... }
 
[Route("MyRoute/{x:length(5)}")]  // Matchesstring value with specified length
public User GetUserByName(string id) { ... }
 
[Route("MyRoute/{x:length(2,100)}")]  // Matches string value with specified length range
public User SetUserByName(string id) { .. }


[Route("MyRoute/{x:minlength(8)}")]  // Matches a string with minimum specified length
public User SetUserByName(string id) { ... }
                                                  

[Route("MyRoute/{x:maxlength(20)}")]  // Matches a string value with specified maximum length
public User SetUserByName(string id) { ... }
 

Other Common Constraints

[Route("MyRoute/{x:min(1)}")]  // Matches integer value with minimum specified value
public Product GetAmount(int id) { ... }
 

[Route("MyRoute/{x:max (100)}")]  // Matches integer with specified maximum value
public Product GetAmount(int id) { ... }
 

[Route("MyRoute/{x:range(1, 100)}")]  // Matches integer with specified range of value
public Product GetProducts(int id) { ... }


[Route("MyRoute/{x:regex(^[a-z][0-9]$)}")]  // Matches value with specified regular expression
public Product GetProducts(int id) { ... }
 

[Route("QuerystringParameterExists?{X})]  // Matched Query string parameter if it exist or not
public Product GetProducts(int id) { ... }


[Route("QuerystringParameterConstraints{x:int}&{y:int}")]  // Matched Query string parameter if they are of specified type or not
public Product GetProducts(int id) { ... }
 

Custom Route Constraints

You can create custom route constraints by implementing the IHttpRouteConstraint interface. For example, the following constraint restricts a parameter to a set of predefined values.


public class CorrectConstraint: IRouteConstraint
{
   private readonly string[] correctOptions;
   public CorrectConstraint(string strOptions)
   {
        correctOptions = strOptions.Split('#');
    }

p        public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
           {
                 object value;
                 if (values.TryGetValue(parameterName, out value) && value != null)
                     {
                  return correctOptions.Contains(value.ToString(), StringComparer.OrdinalIgnoreCase);
                  return false;
             }
}


     To register this value in the Constraint we have to add the code in the Route config as below.

      public static void RegisterRoutes(RouteCollection routes)
       {
               routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        var constraintsResolver = new DefaultInlineConstraintResolver();
        constraintsResolver.ConstraintMap.Add("values", typeof(ValuesConstraint));
        routes.MapMvcAttributeRoutes(constraintsResolver);
  }


And the Route can be called as  below.

     public class BusinessController : Controller
       {
             [Route("ValidRange/{SetValue:values(23#32#34#4435#54#65)}")]
             public ActionResult Show(string scale)
              {
                 return Content("Valid set of value is " + scale);
       }
}

Hope you now Understand that how the constraints in the Attribute based route can be used and applied and how can we create a custom base Route.  Please feel free to provide your valuable comment on the same.

Thanks & Regards
Anil Kumar Pandey
Microsoft MVP, Microsoft MCC, DNS MVM



Monday, September 29, 2014

Attribute Based Routing


Developers who are working on MVC applications are familiar with the word called “Route” or Route table.  A Route is the URL specifying any access or Resource and “Routing” is the way of matching any action to URL. Earlier the Route needs to be added on the Route table and inside the RouteConfig.cs we have to call it. A typical Route was written like this.

routes.MapRoute(
name: "ItemPage",
url: "{itemId}/{itemTitle}",
defaults: new { controller = "Items", action = "Show" },
constraints: new { itemId = "\\d+" }
);      

In MVC 5 there is a new way of defining Rote is added called “Attribute Based Routing”, as the name clearly suggest that the routes are added just like any other attribute in MVC. This does not mean that the conventional based routing the old style is not supported it’s still there.

Now the earlier way of defining rotes were involved in the external routes files with exact controller and action method name. In the attribute based routing the routes are defined directly inside the controls action methods name thus it becomes easier to identify the routes and to the no of lines required to create a route is become a single line only.  We can write the above Rote in the easier way as  

[Route("{itemId:int}/{itemTitle}")]
public ActionResult ShowItem(int itemId)
{
  // Ur code goes here
}

Isn’t it easy? Yes it is, to enable attribute based routing we need to do some change in the Route.config file as follows. Use MapMvcAttributeRoutes as below. 

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");                    

routes.MapMvcAttributeRoutes();
}
} 

The same can also be called inside the RegisterRoute methods with the other conventional routes. So now every method present inside a controller can have its own routes in the form of attribute. Or else if we want that some prefix to be fixed for all the action method present inside a controller then we can define it at the controller level like below.

[RoutePrefix("Sample")]
public class SampleController : Controller
{
// eg.: /sample/Index
public ActionResult Index()
{
         return View();
}

// eg.: /sample/About
public ActionResult About ()
 {
    return View();
  

By this all the Rotes will have a default prefix as “ Sample”. Hope you all have got idea that how to Use Attribute based routing and how to use a default prefix.  Now I will show that if there are some default prefix is set at a controller level but still if I want to override any of the default route then how can we do it.  For doing this we have to again call a specified rote with the Route attribute above the action method where we want to use it with a tilde (~) symbol. For example please check the below sample.

[RoutePrefix("Sample")]
public class SampleController : Controller
{
// eg.: /sample/Index
public ActionResult Index()
{
    return View();
}

// eg.: /sample/About
[Route(“~/Newsample/About”)]
public ActionResult About ()
 {
return View();
 }


Hope you have got idea that how to override the prefix for any specific action method.  Any The Same way we can specify the “Optional Parameter” for any route. Marking a optional parameter is very simple in attribute based routing as we simply need to add one Question mark (?) in the route as below.

public class HomeController : Controller
{
      [Route("home/{id?}")]
      public ActionResult Employee(int id)
      {
           ViewBag.Message = "This is Home Index Page";
           return View();
      }
}

This was simple. Hope You all have liked this article. On the Next article I will discuss about how to use Constraints in the Attribute based Routing. Please feel free to provide your valueable comment on this so that  I can improve more on it.

Thanks & Regards
Anil Kumar Pandey
Microsoft MVP, Microsoft MCC, DNS MVM


 

Kontera