I need to have a variable before every controller.
I want something like that:
www.mysite.com/SYSTEMVARIABLE/Controller/Action
Example:
www.mysite.com/internalevent/Inscricao/Index
www.mysite.com/externalevent/Inscricao/Index
And i need to get the "internalevent" or "externalevent"
Example of values array
At this example, i need the [0] value of array, be the SYSTEMVARIABLE, not "Inscricao (Controller)"
So, how can i get the "SYSTEMVARIABLE" value and work with this ?
I have the following now (not working...)
routes.MapRoute(
name: "Default",
url: "{sistema}/{controller}/{action}/{id}",
defaults: new { controller = "Inscricao", action = "Index", id = UrlParameter.Optional },
constraints: new { sistema = new SistemaRouteConstraint() }
);
I need to get the value from "{sistema}", and i need to do some logic on this value.
Below is the class i want to get it, but ALWAYS the "{sistema}" only gets me the controller value (controller come twice on RouteValueDictionary)
public class SistemaRouteConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
object sistema;
if(values.TryGetValue(parameterName, out sistema) && values != null)
{
var NomeSistema = values["sistema"].ToString();
using (Entities db = new Entities())
{
/*some logic here*/
}
}
return true;
}
}
I am new to asp.net and currently learning url routing
In facebook link(https://www.facebook.com/james.wood) how does james.wood work?
I tried doing the same thing in my asp.net by using this code added in my global
route.MapPageRoute("Profile", "epubtest/profile/{profileid}", "~/epubtest/Profile.aspx");
But I cant make it work with dot like james.wood
Does the dot means another parameter or just one single parameter?
It works without a dot on it just one single word
1) You can add this route:
routes.MapRoute(
name: "User",
url: "{username}",
defaults: new { controller = "Destiny", action = "Index" },
constraints: new { username = new UserNameConstraint() }
);
2) Create this class:
public class UserNameConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
List<string> users = new List<string>() { "username1", "username2" };
var username = values["username"].ToString().ToLower();
return users.Any(x => x.ToLower() == username);
}
}
3) DestinyController
public class DestinyController : Controller
{
public ActionResult Index(string username)
{
return View();
}
}
I hope I've helped.
Hugs!
I have a custom MvcRouteHandler which checks database if a Url exists and pairs it with some controller action and Id.
However if this route handler can not find a matching pair in database, I'd like MVC to keep try with other defined route handlers in route table.
How can I do that?
Update: (Example code added)
routes.MapRoute(
name: "FriendlyRoute",
url: "{FriendlyUrl}").RouteHandler = new FriendlyRouteHandler();
FriendlyRouteHandler is:
public class FriendlyRouteHandler : MvcRouteHandler
{
private TancanDbContext db = new MyDbContext();
protected override IHttpHandler GetHttpHandler(System.Web.Routing.RequestContext requestContext)
{
if (requestContext.RouteData.Values["FriendlyUrl"] != null)
{
string friendlyUrl = requestContext.RouteData.Values["FriendlyUrl"].ToString();
//Here, you would look up the URL Record in your database, then assign the values to Route Data
//using "where urlRecord.Url == friendlyUrl"
try
{
UrlRecord urlRecord = db.UrlRecords.Single(u => u.URL == friendlyUrl);
//Now, we can assign the values to routeData
if (urlRecord != null)
{
requestContext.RouteData.Values["controller"] = urlRecord.Controller;
requestContext.RouteData.Values["action"] = urlRecord.Action;
if(urlRecord.EntityId != null)
requestContext.RouteData.Values["id"] = urlRecord.ObjectId;
}
}
else
{
//Here, I want to redirect to next RouteHandler in route Table
requestContext.RouteData.Values["controller"] = friendlyUrl;
}
}
catch (Exception ex)
{
//throw;
//Here too, I want to redirect to next RouteHandler in route Table
requestContext.RouteData.Values["controller"] = friendlyUrl;
}
}
return base.GetHttpHandler(requestContext);
}
}
After adding this line it seems to work:
requestContext.RouteData.Values["controller"] = friendlyUrl;
Am I lucky or this is right way to do ? Do I need to use IRouteConstraint somewhere?
By the way, my influence was this article by Adam Riddick.
You want to use a custom Constraint, not a custom Handler for this.
routes.MapRoute(
name: "example",
url: "{friendly}",
defaults: new { controller = "FriendlyController", action = "Display" },
constraints: new { friendly = new FriendlyUrlConstraint() }
);
and then the constraint becomes:
public class FriendlyUrlConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
var friendlyUrl = values[parameterName];
// Your logic to return true/false here
}
}
I am using routes.LowercaseUrls = true; in my MVC 4 application which is working great. However, parameters will also get lowercased, so if I have a route like
routes.MapRoute(
name: "MyController",
url: "foo/{hash}/{action}",
defaults: new { controller = "MyController", action = "Details" }
);
The link generated with
#Html.ActionLink("my link", "Details", new { hash=ViewBag.MyHash })
will have the {hash}-part of the URL lowercased as well, for example if ViewBag.MyHash = "aX3F5U" then the generated link will be /foo/ax3f5u instead of /foo/aX3F5U
Is there a way to force MVC to only lowercase the controller and action parts?
For older versions of MVC, the way to go seemed to be to implement a custom subclass of Route, however I don't know how/where to instantiate it, since the signature of the route constructors is quite different to MapRoute and I'm hoping there to be a simpler way.
I think the solution with a custom subclass of Route will be a good enough and simple, but at the same time a little bit ugly :)
You can add a CustomRoute at RegisterRoute method of RouteConfig.cs. Add the following code instead of routes.MapRoute
var route = new CustomRoute(new Route(
url: "{controller}/{action}/{id}",
defaults: new RouteValueDictionary() {
{ "controller", "Home" },
{ "action", "Index" },
{ "id", UrlParameter.Optional }
},
routeHandler: new MvcRouteHandler()
));
routes.Add(route);
Implementaion of particular CustomRoute may look like this:
public class CustomRoute : RouteBase
{
private readonly RouteBase route;
public CustomRoute(RouteBase route)
{
this.route = route;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
values = new RouteValueDictionary(values.Select(v =>
{
return v.Key.Equals("action") || v.Key.Equals("controller")
? new KeyValuePair<String, Object>(v.Key, (v.Value as String).ToLower())
: v;
}).ToDictionary(v => v.Key, v => v.Value));
return route.GetVirtualPath(requestContext, values);
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
return route.GetRouteData(httpContext);
}
}
However it's not an optimal implementation. A complete example could use a combination of extensions on RouteCollection and a custom Route child to keep it as close as possible to the original routes.MapRoute(...) syntax:
LowercaseRoute Class:
public class LowercaseRoute : Route
{
public LowercaseRoute(string url, IRouteHandler routeHandler) : base(url, routeHandler) { }
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
values = new RouteValueDictionary(values.Select(v =>
{
return v.Key.Equals("action") || v.Key.Equals("controller")
? new KeyValuePair<String, Object>(v.Key, (v.Value as String).ToLower())
: v;
}).ToDictionary(v => v.Key, v => v.Value));
return base.GetVirtualPath(requestContext, values);
}
}
RouteCollectionExtensions Class:
public static class RouteCollectionExtensions
{
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")]
public static Route MapLowercaseRoute(this RouteCollection routes, string name, string url)
{
return MapLowercaseRoute(routes, name, url, null /* defaults */, (object)null /* constraints */);
}
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")]
public static Route MapLowercaseRoute(this RouteCollection routes, string name, string url, object defaults)
{
return MapLowercaseRoute(routes, name, url, defaults, (object)null /* constraints */);
}
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")]
public static Route MapLowercaseRoute(this RouteCollection routes, string name, string url, object defaults, object constraints)
{
return MapLowercaseRoute(routes, name, url, defaults, constraints, null /* namespaces */);
}
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")]
public static Route MapLowercaseRoute(this RouteCollection routes, string name, string url, string[] namespaces)
{
return MapLowercaseRoute(routes, name, url, null /* defaults */, null /* constraints */, namespaces);
}
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")]
public static Route MapLowercaseRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces)
{
return MapLowercaseRoute(routes, name, url, defaults, null /* constraints */, namespaces);
}
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")]
public static Route MapLowercaseRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
{
if (routes == null)
{
throw new ArgumentNullException("routes");
}
if (url == null)
{
throw new ArgumentNullException("url");
}
Route route = new LowercaseRoute(url, new MvcRouteHandler())
{
Defaults = CreateRouteValueDictionary(defaults),
Constraints = CreateRouteValueDictionary(constraints),
DataTokens = new RouteValueDictionary()
};
if ((namespaces != null) && (namespaces.Length > 0))
{
route.DataTokens["Namespaces"] = namespaces;
}
routes.Add(name, route);
return route;
}
private static RouteValueDictionary CreateRouteValueDictionary(object values)
{
var dictionary = values as IDictionary<string, object>;
if (dictionary != null)
{
return new RouteValueDictionary(dictionary);
}
return new RouteValueDictionary(values);
}
}
You can now use MapLowercaseRoute instead of MapRoute, so
routes.MapRoute(
name: "MyController",
url: "foo/{hash}/{action}",
defaults: new { controller = "MyController", action = "Details" }
);
simply becomes
routes.MapLowercaseRoute(
name: "MyController",
url: "foo/{hash}/{action}",
defaults: new { controller = "MyController", action = "Details" }
);
exposing the desired behaviour.
Here is one simple way to do this,
public class MyRoute : Route
{
public MyRoute(string url, object defaults): base(url, new RouteValueDictionary(defaults), new MvcRouteHandler())
{
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
if (values["action"] != null)
values["action"] = values["action"].ToString().ToLowerInvariant();
if (values["controller"] != null)
values["controller"] = values["controller"].ToString().ToLowerInvariant();
return base.GetVirtualPath(requestContext, values);
}
}
routes.Add("Default",new MyRoute("{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = MyUrlParameter.Optional }));
See this blog post for detail.
If you look at private RouteCollection.NormalizeVirtualPath method you'll see that it simply uses virtualPath.ToLowerInvariant(). So there is no way to handle that. Even if you create your own route it will be lowercased.
But what you can do is to add hash after '#' sign i.e. "foo/{action}/#{hash}". I haven't
tried, but it should work. Just look at NormalizeVirtualPath implementation.
It's simple as 1.2.3 ,
look at this example
routes.MapRouteLowercase( // changed from routes.MapRoute
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
It's simple to download and install it via Nuget, I use it.
PM> Install-Package LowercaseRoutesMVC
http://lowercaseroutesmvc.codeplex.com/
So I have a method that accepts some JSON data and binds the data to some variables. The route only has the method name in it, nothing else.
Is it possible to have a route constraint that looks at the POST data and checks some variables to determine whether it is the correct route?
The methods:
public ActionResult GetDataV1(string userId)
{
// Do stuff one way
}
public ActionResult GetDataV2(string userId)
{
// Do stuff another way
}
The routes:
routes.MapRoute(
"API GetData V1",
"GetData",
new { controller = "API", action = "GetDataV1" },
new { version = new APIVersionRoutingConstraint("1") }
);
routes.MapRoute(
"API GetData V2",
"GetData",
new { controller = "API", action = "GetDataV2" },
new { version = new APIVersionRoutingConstraint("2") }
);
The client would post { "version":"1", "userId":"mrjohn" } to /GetData and would get response from GetDataV1. The APIVersionRoutingConstraint would make sure the right method is called depending on the version variable.
Would it be good practice to try to deserialise the request stream inside the constraint? Maybe it would be better to put the version in the URL like /GetData/1 and the other variables in the JSON body?
Rather than trying to check the version as a route constraint, why not go to one Action that then checks the version to execute the appropriate work?
I haven't tested this, but the idea is:
public ActionResult GetData(string userId, int version)
{
switch(version)
{
case 1:
return GetDataV1(userId);
case 2:
return GetDataV2(userId);
// You could always add more cases here as you get more versions,
// and still only have the one route
default:
// Probably more appropriate to return a result that contains error info,
// but you get the idea
throw new ArgumentOutOfRangeException("version");
}
}
// Made these private since they are called from the public action
private ActionResult GetDataV1(string userId)
{
// Do stuff one way
}
private ActionResult GetDataV2(string userId)
{
// Do stuff another way
}
And then you only need the one route
routes.MapRoute(
"GetData",
"GetData",
new { controller = "API", action = "GetData" },
new { version = "\d+" } // Require a numeric version
);
Made the APIVersionRoutingConstraint like this:
public class APIVersionRoutingConstraint : IRouteConstraint
{
private string _versionNumber;
public APIVersionRoutingConstraint(string versionNumber)
{
_versionNumber = versionNumber;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
string version = null;
if (httpContext.Request.ContentType.Contains("application/json"))
{
string body = new StreamReader(httpContext.Request.InputStream).ReadToEnd();
httpContext.Request.InputStream.Position = 0;
var vals = new JavaScriptSerializer().DeserializeObject(body) as Dictionary<string, object>;
if (vals.ContainsKey("version"))
version = vals["version"].ToString();
}
// Must have version to pass constraint
return !string.IsNullOrEmpty(version) && version == _versionNumber;
}
}
It's not super-efficient I guess, but it gets the job done. Only the route that matches the version in the POST body gets used.