Can anyone point me in the right direction on how to map a route which requires two guids?
ie. http://blah.com/somecontroller/someaction/{firstGuid}/{secondGuid}
Create a RouteConstraint like the following:
public class GuidConstraint : IRouteConstraint {
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if (values.ContainsKey(parameterName))
{
string stringValue = values[parameterName] as string;
if (!string.IsNullOrEmpty(stringValue))
{
Guid guidValue;
return Guid.TryParse(stringValue, out guidValue) && (guidValue != Guid.Empty);
}
}
return false;
}}
Next when adding the route :
routes.MapRoute("doubleGuid", "{controller}/{action}/{guid1}/{guid2}", new { controller = "YourController", action = "YourAction" }, new { guid1 = new GuidConstraint(), guid2 = new GuidConstraint() });
If you use kazimanzurrashid's code, make sure to include Nikos D's comment. I ended up with this:
public class NonEmptyGuidRouteConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route,
string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if (values.ContainsKey(parameterName))
{
var guid = values[parameterName] as Guid?;
if (!guid.HasValue)
{
var stringValue = values[parameterName] as string;
if (!string.IsNullOrWhiteSpace(stringValue))
{
Guid parsedGuid;
Guid.TryParse(stringValue, out parsedGuid);
guid = parsedGuid;
}
}
return (guid.HasValue && guid.Value != Guid.Empty);
}
return false;
}
}
+1 @kazimanzurrashid. Seems spot on.
I'll give an alternative for those who haven't got C#4.0, of which Guid.TryParse is part of. There's another alternative with Regex but probably not worth the bother.
public class GuidConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if (values.ContainsKey(parameterName))
{
string stringValue = values[parameterName] as string;
if (!string.IsNullOrEmpty(stringValue))
{
//replace with Guid.TryParse when available.
try
{
Guid guid = new Guid(stringValue);
return true;
}
catch
{
return false;
}
}
}
return false;
}
}
Definitely be wary of the code given by @kazimanzurrashid. It was a good start, but it definitely has a bug or too. I was passing a real Guid into the route values (instead of a string of a Guid), and I couldn't get anything to match my route. It took me forever to realize that the GuidConstraint was constraining against a real Guid, if that makes any sense. :)
Here's what I ended up with, which accepts any data type (not just string), is a bit faster (I think), and contains less if block nesting.
public class GuidConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
object value;
if (!values.TryGetValue(parameterName, out value)) return false;
if (value is Guid) return true;
var stringValue = Convert.ToString(value);
if (string.IsNullOrWhiteSpace(stringValue)) return false;
Guid guidValue;
if (!Guid.TryParse(stringValue, out guidValue)) return false;
if (guidValue == Guid.Empty) return false;
return true;
}
}
For MVC 5 there is already implemented the GuidRouteConstraint Class:
https://msdn.microsoft.com/en-us/library/system.web.mvc.routing.constraints.guidrouteconstraint(v=vs.118).aspx
Full list of available MVC constraints:
https://msdn.microsoft.com/en-us/library/system.web.mvc.routing.constraints(v=vs.118).
I found that assuming the type is a Guid causes issue when using things like @Html.RouteLink(...) and in Routing tests where the URL is supplied as a string. The code below caters for these situations. Using the above code samples caused issues in my views and/or tests, this below works fine.
public class GuidConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
var value = values[parameterName];
if (value == null) return false;
var stringValue = value.ToString();
if (string.IsNullOrEmpty(stringValue)) return false;
Guid guidValue;
return Guid.TryParse(stringValue, out guidValue) && (guidValue != Guid.Empty);
}
}