Odd routing outcome with ASP.NET MVC 4? - asp.net-mvc-4

I have the oddest routing problem that I cannot work out how to avoid. We have two areas in our application, and both of them have an Employees controller. So we have these two valid URLs:
blah.com/Employees/Employees
blah.com/Reports/Employees
The routes are registered as follows:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Employees_default",
"Employees/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Reports_default",
"Reports/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
Now everything works as expected, but the problem crops up if someone enters and incorrect URL, like this one:
blah.com/Employees
which then generates the following error:
Multiple types were found that match the controller named 'Employees'. This can happen if the route that services this request ('{controller}/{action}/{id}') does not specify namespaces to search for a controller that matches the request. If this is the case, register this route by calling an overload of the 'MapRoute' method that takes a 'namespaces' parameter.
The request for 'Employees' has found the following matching controllers:
blah.Admin.Areas.Employees.Controllers.EmployeesController
blah.Areas.Reports.Controllers.EmployeesController
It doesn't make sense to me that it would even try to match either of those two routes when the area is missing from the route? The routes clearly include the area name in the route, and it is not optional?
I also noticed that if I have another valid controller for another route, say this one:
blah.com/Tools/ErrorLog
that if I enter blah.com/ErrorLog it actually RUNS that Tools/ErrorLog controller, but it blows up attempting to find the views. Any ideas what is up here?

You need to use NameSpaces in your MapRoute method.
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Employees_default",
"Employees/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
new [] { Mysolution.Myproject.Employees }
);
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Reports_default",
"Reports/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
new [] { Mysolution.Myproject.Reports }
);
}

Related

mvc url.action returning null or url as get

I am using mvc Url.Action as this
<a href="#Url.Action("Index", "Product", new { area = "Product", search = UrlParameter.Optional, categoryId = category.IdCategory })">
I have my routing as:-
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Product_default",
"Product/{controller}/{action}/{id}/{categoryId}",
defaults: new { controller = "Product", action = "Index", search = UrlParameter.Optional, categoryId = #"\d+" },
namespaces: new[] { "IBuyFrontEnd.Areas.Product.Controllers" }
);
}
I cannot get the url to map to this route. I am getting it as
However if i change the action to
#Url.Action("Index", "Product")
I get this as Url
http://localhost/iteric/?action=Index&controller=Product
I cannot figure the why so of this behavior. Just started using .net mvc. Please help me with this.
Ok So figured out that to get Url.Action to generate the url requires the that action to be present in the controller, and also the parameters should be in place. So i just changed my controller action to
public ActionResult Index( int? categoryId,string search)
{
return View();
}

ASP MVC default route is not applying for root site url

I'm having trouble getting ASP.net MVC to serve up the default controllers index view for the root site url http://mysite:8080/. All I get back is a 404 with The resource cannot be found. It works fine if I specify the full route path in the url : http://mysite:8080/Default/Index, however, I want the default route to apply even if the user doesn't specify the route path though. It seems that this should just work out of the gate. This is a fresh project from the VS2013 MVC 4 template.
I've tried both route mappings below and neither seems to work. How can this be achieved?
routes.MapRoute(
"Root",
"",
new { controller = "DefaultController", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "DefaultController", action = "Index", id = UrlParameter.Optional }
);
Here is a solution to this problem. It's disappointing that the default route's defaults do not apply for the root site url though.
routes.Add(new Route("", new SiteRootRouteHandler("~/Default/Index")));
public class SiteRootRouteHandler : IRouteHandler
{
private readonly string _redirectUrl;
public SiteRootRouteHandler(string redirectUrl)
{
_redirectUrl = redirectUrl;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new SiteRootHandler(_redirectUrl);
}
}
public class SiteRootHandler: IHttpHandler
{
private readonly string _redirectUrl;
public SiteRootHandler(string redirectUrl)
{
_redirectUrl = redirectUrl;
}
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
context.Response.RedirectPermanent(_redirectUrl);
}
}

MVC 4 - getting a 404 on a certain area's controllers

My problem is regarding setting up my app's access for internal users and external users.
Based on MS TechKB: AREAS in ASP.NET I went with AREAS. I have an Areas/Internal and Areas/External. Areas/External has the actual functionality and controllers for the app. All Areas/Internal does is check server variables and sets your identity as your domain name if it checks out in active directory.
The problem is, I can get to the Areas/External controllers/pages just fine but when I try to browse to the Areas/Internal area controller I get a 404. My controller is named Intranet (excluding the "Controller" in the name) and this is what my InternalAreaRegistration.cs file looks like:
public override void RegisterArea(System.Web.Mvc.AreaRegistrationContext context)
{
context.MapRoute(
"Internal_default",
"Intranet/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional });
}
public override string AreaName
{
get
{
return "Internal";
}
}
Can anyone tell me why I would be getting a 404 on the internal controllers?
It seems you are missing controller segment after Area in the code you wrote above. It should be something like:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Internal_default",
"Intranet/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "Your Controller Namespace" }
);
}
A related question at below link is answered, hope that will help too:
Controller with same name as an area - Asp.Net MVC4

Create a route with an additional url parameter without creating an area

I'm building a site for a client using .Net MVC 4. The entire site uses the default MVC 4 route and all the pages work fine
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
)
There is a page on the site called 'Teachers'. For this page, there are several links that take you to new pages that are subsets of the 'Teachers' page. The client wants the url structure to appear like this
www.{mysite}.com/School/Teachers/Apply
www.{mysite}.com/School/Teachers/Benefits
I thought I could simple add the Apply and Benefits pages as an ActionResult in my SchoolController then use the routing feature in MVC to map the url to the correct ActionResult method in the SchoolController.
This is my controller:
public class SchoolController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Administration()
{
return View();
}
public ActionResult Teachers()
{
return View();
}
public ActionResult Apply()
{
return View();
}
public ActionResult Benefits()
{
return View();
}
}
This is the custom route that I tried.
routes.MapRoute(
"Teachers",
"{controller}/{page}/{action}",
new { controller = "School", page = "Teachers", action = "Index" }
)
I placed this route before the default route but this adds 'teachers' to every url on the site like this:
www.{mysite}.com/{controller}/teachers/{action}
SUMMARY
All the pages on my site use this url structure:
www.{mysite}.com/{controller}/{action}
This one page, however, has the following structure:
www.{mysite}.com/{controller}/teachers/{action}
How can I do this with routes?
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "TeachersActions",
url: "School/Teachers/{action}",
defaults: new { controller = "School" },
constraints: new { action = "Apply|Benefits" } // actions under Teachers
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index",
id = UrlParameter.Optional }
);
}
Any actions you want to be under Teachers should be added to the route. It is actually not necessary to specify defaults for action or page for this route to work (unless you do have some need for a page route value to be captured). The catch here is that a user can still target the actions under Teachers by entering the URL School/{action} because it is caught by the default route. Now this may or may not be a concern for you. Personally I would not consider it such a big issue since the users should just be using the site's navigation instead of entering the URLs manually.

MVC 4 Routing with UrlParameter.Optional expects parameter

In my MVC solution i have Different areas. One of the area's registration is class is displaied below.
public class CommercialAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Commercial";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Commercial_default",
"Commercial/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
Based on this the url hxxp://localhost:63363/Commercial/VesselManagement should call the VesselManagement controller's Index action method. It did call as expected once in a while. But now it does not execute the action method.
But if i type the Url as hxxp://localhost:63363/Commercial/VesselManagement/index/abc the Action method Index is called and the parameter abs is passed.
Not only to this action method but to all action methods in the whole application the url has to be called with in this pattern. What could be the issue. Thank you all in advance for the help.
Note: I have used hxxp insted of http
The Global.asx
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
//Configure FV to use StructureMap
var factory = new StructureMapValidatorFactory();
//Tell MVC to use FV for validation
ModelValidatorProviders.Providers.Add(new FluentValidationModelValidatorProvider(factory));
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
}
}
The VesselManagement Index() Action
public ActionResult Index()
{
InitialData();
return View();
}
NOTE: Just now i noticed that index does not take any parameters but i know that wouldn'd effect the routing.
I am really sorry the issue was due to a mistake by one of my colleague where he created an Area and for the area registration he has mentioned the routing rule incorrectly.
public class SharedAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Shared";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Shared_default",
"{controller}/{action}/{id}", //<---- This made the issue with routing
new { action = "Index", id = UrlParameter.Optional }
);
}
}
How i figured out the mistake ill explain the steps below so that it would be helpful.
First i installed the Route Debugger
Then Wrote the Application_Error in the Global.asx to handle the error. if this is not written the Route Debugger will not be able to find the routing.
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
// Log the exception.
//ILogger logger = Container.Resolve<ILogger>();
// logger.Error(exception);
Response.Clear();
HttpException httpException = exception as HttpException;
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "Error");
if (httpException == null)
{
routeData.Values.Add("action", "Index");
}
else //It's an Http Exception, Let's handle it.
{
switch (httpException.GetHttpCode())
{
case 404:
// Page not found.
routeData.Values.Add("action", "HttpError404");
break;
case 500:
// Server error.
routeData.Values.Add("action", "HttpError500");
break;
// Here you can handle Views to other error codes.
// I choose a General error template
default:
routeData.Values.Add("action", "General");
break;
}
}
// Pass exception details to the target error View.
routeData.Values.Add("error", exception);
// Clear the error on server.
Server.ClearError();
// Avoid IIS7 getting in the middle
Response.TrySkipIisCustomErrors = true;
// Call target Controller and pass the routeData.
//IController errorController = new ErrorController();
//errorController.Execute(new RequestContext(
// new HttpContextWrapper(Context), routeData));
}
Then i typed the URL hxxp://localhost:63363/Commercial/VesselManagement and the output was
In the output route data and data token clearly says it tries to find the commercial controller and VesselManagement ACtion inside Shared area.
Reason for the error is Shared Area routing incorrectly specified and when that is corrected by addeing Share in frount it ws resolved.
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Shared_default",
"Shared/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
Thanks to RouteDebugger
NOTE: Routing works top to bottom and the moment it finds a matching route it will ignore the rest.