I have a MVC.NET application that used the default routing engine to connect {controller}/{action}/{*pathInfo} and everything works fine. I've added some WCF services to the project and need to setup specific routes to reach them. The problem I'm running into is when I add the specific routes, the default automatic routing no longer functions. How can I either use the default route handler with some overrides, or if that is not possible setup a generic route handler that automatically handles all existing Controllers/Actions and static resources?
public static void RegisterRoutes(RouteCollection routes)
{
routes.Add(new ServiceRoute("services/rest/InvoiceService", new WebServiceHostFactory(), typeof(InvoiceService)));
routes.Add(new ServiceRoute("services/soap/InvoiceService", new ServiceHostFactory(), typeof(InvoiceService)));
routes.Add(new ServiceRoute("services/soap/LoginService", new ServiceHostFactory(), typeof(LoginService)));
routes.Add(new ServiceRoute("services/rest/LoginService", new WebServiceHostFactory(), typeof(LoginService)));
/** WHAT GOES HERE? **/
routes.MapHttpRoute("Default", "{controller}/{action}/{*pathInfo}",
new { controller = "Home", action = "Index", id = "" });
}
protected void Application_Start()
{
//RouteConfig.RegisterRoutes(RouteTable.Routes); //OLD code that automatically handled routing
RegisterRoutes(RouteTable.Routes); //NEW code
}
I figured it out - for posterity, you need to add a constraint clause to the MapRoute MVC handler that excludes the pattern you have for the WCF services. Here is the final code block that worked. The key part was the REGEX to exclude services as a valid controller name.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { controller = #"^(?!services)\w+$" }
);
RouteTable.Routes.Add(new ServiceRoute("services/rest/InvoiceService", new WebServiceHostFactory(), typeof(InvoiceService)));
RouteTable.Routes.Add(new ServiceRoute("services/soap/InvoiceService", new ServiceHostFactory(), typeof(InvoiceService)));
RouteTable.Routes.Add(new ServiceRoute("services/soap/LoginService", new ServiceHostFactory(), typeof(LoginService)));
RouteTable.Routes.Add(new ServiceRoute("services/rest/LoginService", new WebServiceHostFactory(), typeof(LoginService)));
}
Related
I have the following in my RouteConfig.cs file
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
// route default URL to index.aspx
routes.MapPageRoute(
routeName: "LoginPageRoute",
routeUrl: "login",
physicalFile: "~/Login.aspx"
);
routes.MapPageRoute("LoginPageRoute2", "login2", "~/Login.aspx");
}
However if I try to access my WebApp using "login" or "login2" I get a resource cannot be found error message.
http://localhost:4200/login2 - fails
http://localhost:4200/login - fails
http://localhost:4200/Login.aspx - loads fine
My Global.asax has the following
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
A comment from somebody under my question guided me to the solution which was to move my custom routes ahead of the default routes.
Adding the following to Global.asax helped prove that this was indeed the issue.
public override void Init()
{
base.Init();
this.AcquireRequestState += showRouteValues;
}
protected void showRouteValues(object sender, EventArgs e)
{
var context = HttpContext.Current;
if (context == null)
return;
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(context));
}
Putting a breakpoint on
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(context));
Showed that my "login" mapping was using the default route mapping instead of my custom route mapping.
Thanks again #mason!
I am trying to reach a specific webapi action using: api/error/LogJsError with some formdatacollection. I have an ErrorController like this:
public class ErrorController : ApiController
{
[System.Web.Http.HttpPost]
public void LogJsError(FormDataCollection form)
{
var s = form.Aggregate("Javascript error: message", (current, i) => current + (": " + i.Value));
new Logger(HttpContext.Current).LogException(new Exception(s));
}
}
and the routes are configured like this:
config.Routes.MapHttpRoute(
name: "ApiWithAction",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
In WebApiConfig.Register and
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
inside the RouteConfig.RegisterRoutes
But whatever i do the routecollection returns controller = "Api" which thus results in a 404. What am i doing wrong, why is the api route not uses?
Duuuh...i found the answer right after posting it on: https://msdn.microsoft.com/en-us/library/cc668201%28v=vs.140%29.aspx
Note this: "To avoid having the wrong handler handle a request, you must consider all these conditions when you define routes. The order in which Route objects appear in the Routes collection is significant. Route matching is tried from the first route to the last route in the collection. When a match occurs, no more routes are evaluated. In general, add routes to the Routes property in order from the most specific route definitions to least specific ones."
My MVC route (eg: {controller}/{action}/{id}) was added to the routecollection before the (more specific) WebApi route (eg: api/{controller}/{action}/{id}). So instead of using:
RouteConfig.RegisterRoutes(RouteTable.Routes);
GlobalConfiguration.Configure(WebApiConfig.Register);
In Application_Start(), use:
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
I am new to Asp.net MVC.
I am creating web application, where i have to rewrite url with product name.
I am not sure if that is possible or not in MVC.
Like,
http://sitename.com/category1/product1
http://sitename.com/category1/product2
will have same page.
There are facilities to generate friendly urls within MVC.
Check out the article at - http://www.asp.net/mvc/tutorials/older-versions/controllers-and-routing/asp-net-mvc-routing-overview-cs - for an overview of how this is handled in MVC.
Essentially, you need to configure the routes on application startup as follows. This can usually be done in the global.asax file but cna be split for areas etc.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes); // Reigster the routes
}
This is the default route but you can define as required.
This is probably a duplicate to many but the obvious answers in them do not solve my problem.
I get:
Multiple types were found that match the controller named 'Home'. 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 'Home' has found the following matching controllers:
App.Web.Controllers.HomeController
App.Web.Areas.Mobile.Controllers.HomeController
I've setup a default namespace for my HomeController in Global.ascx.cs:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
new string[] { "App.Web.Controllers.HomeController" }
);
(Verified that App.Web.Controllers.HomeController is not a typo).
And also registered the Mobile's HomeController in MobileAreaRegistration:
public override void RegisterArea(AreaRegistrationContext context) {
context.MapRoute(
"Mobile_default",
"Mobile/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Therefore, why is it that I still see the error message? I've built/cleaned and ran again. Still the same outcome.
This is how I register my routes:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
In your Global.asax route registration for obvious reasons replace:
new string[] { "App.Web.Controllers.HomeController" }
with:
new string[] { "App.Web.Controllers" }
That's a namespace constraint that you should use there, not a specific type.
In my ASP.NET MVC3 App I try simulate "routes.IgnoreRoute("...")"
I create CustomMvcRouteHandler:
public class CustomMvcRouteHandler: MvcRouteHandler
{
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
// do something
....
return base.GetHttpHandler(requestContext);
}
}
in my Global.asax.cs file i have:
protected void Application_Start()
{
// ............
RegisterRoutes(RouteTable.Routes);
// ............
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("elmah.axd");
//routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
).RouteHandler = new CustomMvcRouteHandler();
}
How can I do this ?
I'm not entirely sure what you mean in your question, but I'll try to answer it...
To simulate an IgnoreRoute all you need to do is associate an instance of the StopRoutingHandler from your route. If you're using the built-in ASP.NET "Route" class then you would do something like this:
routes.MapRoute(
"Ignore-This", // Route name
"ignore/{this}/{pattern}" // URL with parameters
).RouteHandler = new StopRoutingHandler();
Anything that matches that pattern will cause the routing system to immediately stop processing any more routes.
If you want to write your own custom route (for example, a new route type that derives from RouteBase), then from its GetRouteData method you need to return the StopRoutingHandler.
#Eilon is the correct answer. Here is an alternative syntax that feels more MVCish.
routes.Add("Ignore-This",
new Route(
"ignore/{this}/{pattern}",
new StopRoutingHandler())
);