Constraints in route - c#

So I have ASP.NET MVC application. I would like to configure its routes. Here is my RouteConfig's code:
public static void Register(RouteCollection routes, bool useAttributes = true)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("favicon.ico");
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
This route works fine. Besides I have an area in my application and try to configure its routes too. It is my area registration code:
public override void RegisterArea(AreaRegistrationContext context)
{
try
{
context.MapRoute(
name: "SiteSettings_Controller",
url: "SiteSettings/{controller}/{action}/{id}",
defaults: new {action = "Index", id = UrlParameter.Optional,
// here I tried to use #"(UserManagement|Tools|Settings)"
//as constraint but it takes no effect
constraints: new {controller = "UserManagement|Tools|Settings" }
);
}
catch (Exception e)
{
// here I get InvalidOperationException ""
}
}
I would like to restrict controllers in SiteSettingsArea's route but when I go to "localhost/SiteSettings/UserManagement" url I get InvalidOperationException with message "No route in the route table matches the supplied values". I believe that this url corresponds to SiteSettings_Controller route but obviously I am wrong. How could I limit controllers in the route properly?

If you search your codebase for SiteSettings_Controller does it appear anywhere else?
The below code certainly worked for me when I just tested it.
using System;
using System.Web.Mvc;
namespace WebApplication1.Areas.SiteSettings
{
public class SiteSettingsAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "SiteSettings";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
name: "SiteSettings_Controller",
url: "SiteSettings/{controller}/{action}/{id}",
defaults: new
{
action = "Index",
id = UrlParameter.Optional
},
constraints: new { controller = "UserManagement|Tools|Settings" }
);
}
}
}

Related

Routing with only {id} parameter returns "resource cannot be found" error

I'm trying to setup routing as follows.
Right now My URL looks like www.mysite.com/Products/index/123
My goal is to setup URL like www.mysite.com/123
Where: Products is my controller name , index is my action name and 123 is nullable id parameter.
This is my route :
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"OnlyId",
"{id}",
new { controller = "Products", action = "index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
and this is my Action Method
public ActionResult index (WrappedViewModels model,int? id)
{
model.ProductViewModel = db.ProductViewModels.Find(id);
model.ProductImagesViewModels = db.ProductImagesViewModels.ToList();
if (id == null)
{
return HttpNotFound();
}
return View(model);
}
this is my model wrapper :
public class WrappedViewModels
{
public ProductViewModel ProductViewModel { get; set; }
public ProductImagesViewModel ProductImagesViewModel { get; set; }
public List<ProductImagesViewModel> ProductImagesViewModels { get; set;
}
Error is thrown on this URL : www.mysite.com/123
The question is:
Why my view returns this error and how to avoid this behavior?
Thanks in advance.
In RegisterRoutes you need to specify little bit more.
routes.MapRoute(
name: "OnlyId",
url: "{id}",
defaults: new { controller = "Products", action = "index" },
constraints: new{ id=".+"});
and then you need to specify the routing of each anchor tag as
#Html.RouteLink("123", routeName: "OnlyId", routeValues: new { controller = "Products", action = "index", id= "id" })
I think this will resolve you immediate.
If you're sure that id parameter is nullable integer value, place a route constraint with \d regex like this, so that it not affect other routes:
routes.MapRoute(
name: "OnlyId",
url: "{id}",
defaults: new { controller = "Products", action = "index" }, // note that this default doesn't include 'id' parameter
constraints: new { id = #"\d+" }
);
If you're not satisfied for standard parameter constraints, you can create a class which inheriting IRouteConstraint and apply that on your custom route as in this example:
// adapted from /a/11911917/
public class CustomRouteConstraint : IRouteConstraint
{
public CustomRouteConstraint(Regex regex)
{
this.Regex = regex;
}
public CustomRouteConstraint(string pattern) : this(new Regex("^(" + pattern + ")$", RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.IgnoreCase))
{
}
public Regex Regex { get; set; }
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if (routeDirection == RouteDirection.IncomingRequest && parameterName == "id")
{
if (values["id"] == UrlParameter.Optional)
return true;
if (this.Regex.IsMatch(values["id"].ToString()))
return true;
// checking if 'id' parameter is exactly valid integer
int id;
if (int.TryParse(values["id"].ToString(), out id))
return true;
}
return false;
}
}
Then place custom route constraint on id-based route to let other routes work:
routes.MapRoute(
name: "OnlyId",
url: "{id}",
defaults: new { controller = "Products", action = "index", id = UrlParameter.Optional },
constraints: new CustomRouteConstraint(#"\d*")
);
I think you missed Routing order. So create first route definition which handles all available controllers and then define one which will handle the rest of the requests, say, one that handles the www.mysite.com/{id} kind of requests.
So swap the OnlyId and Default rules and No more changes required. I believe It should work fine now.

Show views based on authentication status in asp.net mvc

If user is logged in I want to show my department view, if not logged in want to show login page. I have tried something like this inside my RouteConfig
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
if (HttpContext.Current.User==null)
{
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Account", action = "Login", id = UrlParameter.Optional }
);
}
else
{
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Department", action = "Index", id = UrlParameter.Optional }
);
}
}
But this one always loads login page at startup.Can anyone point out what I am doing wrong here?
Note: I am using Asp.net Identity for this application
Your HttpContext.Current.User==null logic would go in the controller, not your route registration
Note- the correct call is Request.IsAuthenticated
Assuming you have an action method like this:
public ViewResult Index()
{
if(Request.IsAuthenticated)
return new RedirectResult("toLoginPage")
else
return new View("loggedInView");
}
However, I believe the [Authorize] attribute could be what you want in your use case: (note - having re-read the question, this may not be accurate, as you want to return a different view based on login status)
[Authorize]
public ViewResult ShowPerson(int id)
{
return new View("loggedInView");
}
And in your web.config, something like
<system.web>
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" />
</authentication>
</system.web>
In this particular instance, with the [Authorize] attribute above the action method, if the user is not logged in, they'd be redirected to log in.
Create your own Authorization attribute:
public class CustomAuthorize: AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if(filterContext.HttpContext.User.Identity.IsAuthenticated)
{
base.HandleUnauthorizedRequest(filterContext);
}
else
{
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary(new{ controller = "Error", action = "AccessDenied" }));
}
}
}
Then add [CustomAuthorize] to your controller and change the route it points to.
This was taken from here
You can achieve this with route constraints:
public class DelegateConstraint : IRouteConstraint
{
private readonly Func<HttpContextBase, bool> _isMatch;
public DelegateConstraint(Func<HttpContextBase, bool> isMatch)
{
_isMatch = isMatch;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
return _isMatch(httpContext);
}
}
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "CustomAuth1",
url: "AuthArea/{action}/{id}",
defaults: new { controller = "Department", action = "Index", id = UrlParameter.Optional },
constraints: new { auth = new DelegateConstraint(httpContext => !httpContext.Request.IsAuthenticated) }
);
routes.MapRoute(
name: "CustomAuth2",
url: "AuthArea/{action}/{id}",
defaults: new { controller = "Account", action = "Index", id = UrlParameter.Optional },
constraints: new { auth = new DelegateConstraint(httpContext => httpContext.Request.IsAuthenticated) }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
In this example the ~/AuthArea url will be resolved by Account or Department controller depending on the Request.IsAuthenticated property.
UPDATE:
This way you get a complete routing capability, but still need to specify the correct controller:
#Html.ActionLink("Context dependent link", "Index", #Request.IsAuthenticated ? "Account" : "Department")
This link would always be rendered as:
Context dependent link

Route Mapping not working with "area"

I've been reading a lot about this problem, and I can't figure this out.
Everything is pretty straightforward with routing and ASP .NET MVC, but I'm stuck with this.
The problem is that I'm trying to make a GET to a given url with this form:
{area}/{controller}/{action}
But the {area} is not being registered. My default route is not working either (not working in the sense that I need to go to localhost:port/Home instead of just going to localhost:port/
This is my code:
RouteConfig:
public class RouteConfig
{
public static void RegisterRoute(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
route.MapRoute(
"Default",
"{controller}/{action}",
new {controller = "Home", action = "Index"}
);
}
}
This is the Area that is not being registered:
public class TransaccionesAreaRegistration : AreaRegistration
{
public override string AreaName
{
get{
return “Transacciones”;
}
}
public override void RegisterArea(AreaRegistrationContext context){
context.MapRoute(
"Transacciones_default",
"Transacciones/{controller}/{action}/{id}",
new { controller = "Transacciones", action = "Index", id = UrlParameter.Option}
);
}
}
Finally, this is my global.asax (I do call AreaRegistration.RegisterAllAreas() method):
protected void Application_Start(){
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
I will really appreciate some advice with this, I think I have spent enough time googling :O)
Just try this
RouteConfig:
public class RouteConfig
{
public static void RegisterRoute(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
route.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new {controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
This is the Area that is not being registered:
public class TransaccionesAreaRegistration : AreaRegistration
{
public override string AreaName
{
get{
return “Transacciones”;
}
}
public override void RegisterArea(AreaRegistrationContext context){
context.MapRoute(
“Transacciones_default”,
“Transacciones/{controller}/{action}/{id}”,
new { action = ”Index”, id = UrlParameter.Optional },
new string[] { "MyApp.Transacciones.Controllers" } // specify the new namespace
);
}
}
------------------------------OR Try This--------------------------------
public class RouteConfig
{
public static void RegisterRoute(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
AreaRegistration.RegisterAllAreas();
route.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new {controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
Reason why default route is not working
Because you never registered a default one. Add this line in the RouteConfig -
routes.MapRoute("Home", "", new { Controller = "Home", Action = "Index" });
So the final code should look like this -
public class RouteConfig
{
public static void RegisterRoute(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
routes.MapRoute("Home", "", new { Controller = "Home", Action = "Index" });
route.MapRoute(
"Default",
"{controller}/{action}",
new {controller = "Home", action = "Index"}
);
}
}
Possible reason why Area seems not working
May be the same reason also the Area registration seems not working. Try adding the following line in area registration -
routes.MapRoute("Transacciones_Home", "Transacciones", new { Controller = "Transacciones", Action = "Index" });
So it looks like -
public class TransaccionesAreaRegistration : AreaRegistration
{
public override string AreaName
{
get{
return “Transacciones”;
}
}
public override void RegisterArea(AreaRegistrationContext context){
routes.MapRoute("Transacciones_Home", "Transacciones", new { Controller = "Transacciones", Action = "Index" });
context.MapRoute(
“Transacciones_default”,
“Transacciones/{controller}/{action}/{id}”,
new { controller = “Transacciones”, action = ”Index”, id = UrlParameter.Option}
);
}
}
}
This questions was the one that helped me.
The thing is, the order in the routes' registration is very important. Considering that, I started checking my other areas registration, and I found out that all the requests where falling into the first rule that was a general rule like this:
routes.MapRoute(
name : "Default",
url : {controller}{action}{id}
);
So, after that rule, none of the rules were being considered.
Thanks everyone for trying to help, bests!

How to convert query string parameters to route in asp.net mvc 4

I have a blogging system that I'm building and I can't seem to get ASP.NET MVC to understand my route.
the route I need is /blogs/student/firstname-lastname so /blogs/student/john-doe, which routes to a blogs area, student controller's index action, which takes a string name parameter.
Here is my route
routes.MapRoute(
name: "StudentBlogs",
url: "blogs/student/{name}",
defaults: new { controller = "Student", action="Index"}
);
My controller action
public ActionResult Index(string name)
{
string[] nameparts = name.Split(new char[]{'-'});
string firstName = nameparts[0];
string lastName = nameparts[1];
if (nameparts.Length == 2 && name != null)
{
// load students blog from database
}
return RedirectToAction("Index", "Index", new { area = "Blogs" });
}
But it won't seem to resolve...it works fine with /blogs/student/?name=firstname-lastname, but not using the route I want, which is /blogs/student/firstname-lastname. Any advice on how to fix this would be greatly appreciated.
My RouteConfig
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "StudentBlogs",
url: "blogs/student/{name}",
defaults: new { controller = "Student", action = "Index"},
constraints: new { name = #"[a-zA-Z-]+" },
namespaces: new string[] { "IAUCollege.Areas.Blogs.Controllers" }
);
routes.MapRoute(
name: "Sitemap",
url :"sitemap.xml",
defaults: new { controller = "XmlSiteMap", action = "Index", page = 0}
);
//CmsRoute is moved to Gloabal.asax
// campus maps route
routes.MapRoute(
name: "CampusMaps",
url: "locations/campusmaps",
defaults: new { controller = "CampusMaps", action = "Index", id = UrlParameter.Optional }
);
// core route
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
// error routes
routes.MapRoute(
name: "Error",
url: "Error/{status}",
defaults: new { controller = "Error", action = "Error404", status = UrlParameter.Optional }
);
// Add our route registration for MvcSiteMapProvider sitemaps
MvcSiteMapProvider.Web.Mvc.XmlSiteMapController.RegisterRoutes(routes);
}
}
You have to declare custom routes before the default routes. Otherwise it will be mapping to {controller}/{action}/{id}.
Global.asax typically looks like this:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
If you created an Area named Blogs, there is a corresponding BlogsAreaRegistration.cs file that looks like this:
public class BlogsAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Blogs";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Blogs/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
Hyphens are sometimes treated like forward slashes in routes. When you are using the route blogs/students/john-doe, my guess is that it is matching the Area pattern above using blogs/students/john/doe, which would result in a 404. Add your custom route to the BlogsAreaRegistration.cs file above the default routes.
Try adding the parameter to the route:
routes.MapRoute(
name: "StudentBlogs",
url: "blogs/student/{name}",
defaults: new { controller = "Student", action="Index", name = UrlParameter.Optional}
);
Try adding a constraint for the name parameter:
routes.MapRoute(
name: "StudentBlogs",
url: "blogs/student/{name}",
defaults: new { controller = "Student", action="Index" },
constraints: new { name = #"[a-zA-Z-]+" }
);
Dashes are a bit weird in MVC at times.. because they are used to resolve underscores. I will remove this answer if this doesn't work (although.. it should).
This has the added benefit of failing to match the route if a URL such as /blogs/student/12387 is used.
EDIT:
If you have controllers with the same name.. you need to include namespaces in both of your routes in each area. It doesn't matter where the controllers are.. even if in separate areas.
Try adding the appropriate namespace to each of the routes that deal with the Student controller. Somewhat like this:
routes.MapRoute(
name: "StudentBlogs",
url: "blogs/student/{name}",
defaults: new { controller = "Student", action="Index" },
namespaces: new string[] { "Website.Areas.Blogs.Controllers" }
);
..and perhaps Website.Areas.Admin.Controllers for the one in the admin area.

Areas in asp.net mvc, only one work at time

My folder look like this:
(root)/Areas/Admin/Views/..
(root)/Areas/Admin/Controllers/...
(root)/Areas/Admin/Routes.cs
(root)/Areas/Forum/Views/..
(root)/Areas/Forum/Controllers/...
(root)/Areas/Forum/Routes.cs
public class Routes : AreaRegistration
{
public override string AreaName
{
get { return "Admin"; }
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_Default",
"{controller}/{action}/{Id}",
new { controller = "Admin", action = "Index", Id = (string)null }
);
}
}
public class Routes : AreaRegistration
{
public override string AreaName
{
get { return "Forum"; }
}
public override void RegisterArea(AreaRegistrationContext routes)
{
routes.MapRoute(
"Forum_Default",
"{controller}/{action}",
new { controller = "Forum", action = "Index"}
);
}
}
Global.asax
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
AreaRegistration.RegisterAllAreas();
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
The startpage should be Home/Index but it start with Admin/Index, why?
Only site.com/Admin works not site.com/Forum
How should i get Admin and Forum Areas to work right? Why is only Admin working and not Forum?
When i delete Admin/Routes.cs file Forum start to work...
EDIT:
Home in ~/Views/ don't show as startpage even if i have
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
in my Global.asax after AreaRegistration.RegisterAllAreas();
I believe your area mappings should be structured like so.
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_Default",
"Admin/{controller}/{action}/{Id}",
new { controller = "Admin", action = "Index", Id = (string)null }
);
}
and
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Forum_Default",
"Forum/{controller}/{action}/{Id}",
new { controller = "Forum", action = "Index"}
);
}
Keeps your routes from conflicting, which is what i think is happening in your case. As your default route matches your admin route.

Categories

Resources