Why am I getting 404 when the route matches? ASP.Net MVC - asp.net

I have been stuck on this issue for several hours now.
I have a controller called 'DecisionPoint' and I have a breakpoint set on it's 'ApplicationState' action. No matter what I try I keep getting a 404 in the browser. I suspected that my route was not correct so I downloaded a route debugger and it turns our the URLs I am trying match the Controller and the action. So why do I get a 404 and never see the breakpoint hit?
/DecisionPoint/ApplicationState/no/worky --> 404
Controller:
public ActionResult ApplicationState(string fileName, string stateString)
{
string filePath = GetDpFilePath(fileName);
HtmlDocument htmlDocument = new HtmlDocument();
htmlDocument.Load(filePath);
HtmlNode stateScriptNode =
htmlDocument.DocumentNode.SelectSingleNode("/html/head/script[#id ='applicationState']");
stateScriptNode.InnerHtml = "var applicationStateJSON =" + stateString;
htmlDocument.Save(filePath);
return Json("State Updated");
Route
routes.MapRoute(
"DecisionPointState", // Route name
"DecisionPoint/ApplicationState/{fileName}/{stateString}", // URL with parameters
new {controller = "DecisionPoint", action = "ApplicationState"} // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}`
**Update**
I create a whole new controller and it works. This is now what my route table looks like. The state controller correclty routes to SaveState
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"StateRoute", // Route name
"State/SaveState/{file}/{state}", // URL with parameters
new { controller = "State", action = "SaveState", file = UrlParameter.Optional, state = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"DPStateRoute", // Route name
"DecisionPoint/ApplicationState/{file}/{state}", // URL with parameters
new { controller = "DecisionPoint", action = "ApplicationState", file = UrlParameter.Optional, state = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
// RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
}
}
So I am stumped..

Make sure your controller class is called DecisionPointController and not DecisionPoint

If your AreaRegistration class and Controller class are not in the same namespace, you'll be in this situation - the route will match (and RouteDebugger will confirm this) but you'll get a 404 at the "correct" url.
The solution is to make sure both clases are in the same namespace.

This can happen due to your Controller being in another project which references a different version of System.Web.Mvc!
I had the same symptoms - the correct route was found, but got a 404! On HttpHandler.ProcessRequest, an exception was thrown saying the handler's controller does not implement IController.

I just recently ran into this same problem. For me the problem was that I had two controllers with the same name if different namespaces (One was in an area).

Dumb, but it got me:
Be sure your controller inherits from Controller

I'm no routes guru, but I have always added all parameters for the default argument:
routes.MapRoute(
"DecisionPointState", // Route name
"DecisionPoint/ApplicationState/{fileName}/{stateString}", // URL with parameters
new {controller = "DecisionPoint",
action = "ApplicationState"
fileName = UrlParameter.Optional,
stateString = UrlParameter.Optional
} // Parameter defaults
);

In my case, the answer for the same problem was a matter of needing to "include in project" the relevant controllers and views instead of incorrect routing rules.
When mine were created, they weren't automatically included for some reason. This problem was revealed after I closed and re-opened the solution.
{+1 hate} awarded to Visual Studio for its faulty hyper-automation sending me digging through Web.Config files, trying to tack on extensions, and even trying (and failing) to whip up a decent ErrorController.

I was missing
app.MapControllers();
in my program.cs (.NET 6 and above)

Related

ASP.NET routes not triggering as I expect

trying to get started with ASP.NET MVC.
I encountered a few difficulties while setting up basic routes.
My routes are as follows:
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 }
);
routes.MapRoute(
name: "ImproItem",
url: "{controller}/{action}/{type}",
defaults: new { controller = "ImproItemForm", action = "Index", type = UrlParameter.Optional }
);
}
My view calls :
<li>#Html.ActionLink("linkLabel", "Index", "ImproItemForm", new { type = "blablabla" }, null)</li>
My controller action sig is:
public class ImproItemFormController : Controller
{
...
public ActionResult Index(String t)
{
...}
}
The view generates the following HTML:
<li>linkLabel</li>
This looks OK to me.
However, this link correctly calls the controller's action (using ImproItem route) but it does not pass the blablabla argument. The parameter t = null when I debug the app.
Can you explain me why?
What should I change to correctly receive the blablabla argument?
Now if I start the application and try to browse
Also is it normal that when I browse:
http://localhost:55193/ImproItemForm/Index?id=foobar
It does call the ImproItemFormController.Index(String t) method ?
I did not expect that this URL would match with this route:
routes.MapRoute(
name: "ImproItem",
url: "{controller}/{action}/{type}",
defaults: new { controller = "ImproItemForm", action = "Index", type = UrlParameter.Optional }
);
I thought the argument needs to have the same name than in the route : type and not id.
Thx in advance for your help.
I thought the argument needs to have the same name than in the route :
type and not id.
Actually when you request the URL - http://localhost:55193/ImproItemForm/Index?id=foobar, it actually calls the Default route only, and not the custom route that you have created. Default route has - controller name, action name and id. That means, if there is any URL matching this pattern (i.e., {controllername}/{actionname}/{id}) would match the default route.
Order of routes are very important in route collection because route table is built top-to-bottom, so as soon as the URL finds its first matching route, it stops scanning further.So ideally, default route should be the bottom most route in route collection.
I guess all your problems for this particular scenario should be resolved by performing following two steps-
Move default route to the bottom in route collection in RouteConfig.cs
Rename parameter "t" to "type" in Index action.
Change the name of the parameter in your action to match the name in the ActionLink
public ActionResult Index(String type)

RedirectToAction() generates wrong URL

I'm having a problem with MVC 4, and I guess it's something really trivial, but it's been bugging me for the last day and I can't seem to figure it out.
I have this url:
http://www.example.com/my-dashed-url
I have a Controller named:
public class MyDashedUrlController: Controller
{
}
I have only two Routes like this:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("my-dashed-url",
"my-dashed-url/{action}",
new { controller = "MyDashedUrl", action = "Index" });
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home",
action = "Index",
id = UrlParameter.Optional }
);
I get to the index just fine. However, when I do this:
public ActionResult Index()
{
if (NoUserIsLoggedOn)
return RedirectToAction("Logon", "MyDashedUrl");
return View();
}
public ActionResult Logon()
{
Contact c = GetContact();
return View(c);
}
It doesn't redirect me to the "Logon" action properly.
It should redirect me to:
http://www.example.com/my-dashed-url/logon
but instead it tries to redirect me to:
http://www.example.com/logon
... which doesn't work (404 Not Found)
I'm missing something. Can anyone spot it? If anyone needs any more information, let me know.
And it's EVERY RedirectToAction that does the same thing in this controller. A Html.BeginForm("Logon", "MyDashedUrl") would also generate:
http://www.example.com/logon
I guess it has to do something with the Routes I defined, but I can't find the faulty one, seeing as they're all the same. If I disable all of my Routes besides the default one from MVC, the problem remains the same
Make sure that you have declared this custom route BEFORE the default one:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"my-dashed-url",
"my-dashed-url/{action}",
new { controller = "MyDashedUrl", action = "Index" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
Remember that routes are evaluated in the order you declared them. So the first route that matches a request will be used. If you declare your custom route after the default one, it is the default route that will match the request.

Url.Action does not use desired route for output

I have the following route definitions in my MVC3 website:
routes.MapRoute(
"FB", // Route name
"fb/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
).RouteHandler = new RH();
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
My custom "RH" handler's code is
public class RH : MvcRouteHandler
{
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
//here I store somewhere that 'fb' prefix is used, so logic is different in some places
return base.GetHttpHandler(requestContext);
}
}
What I want to achieve, that when my website is accessed with the 'fb' subpath-prefix, then my website-logic executes a little bit different way.
The problem is, that when I access my site normally (eg. http://localhost), then when I execute
Url.Action('action' 'controller')
, then the output is "http://localhost/fb/controller/action".
I want to achieve, that when my site was accessed with 'fb' prefixed subpath, then my Url.Action calls output /fb/controller/action path, and if I access the website normally (without 'fb' prefix subpath), then Url.Action calls output /controller/action
The main thing, that /fb/controller/actions have to route to the same controllers/actions as when the site is accessed via /controller/action format.
The 'fb' route is just needed to store some temporary info when 'fb' prefix i used.
Seems I found a solution based on this link (MVC 3 Routing and Action Links not following expected contextual Route), new path-placeholder introduced and constraint added.
Maybe it's not good enough, or you know better than this, but seems to work for me:
routes.MapRoute(
"FB", // Route name
"{path}/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
new { path = "fb" }
).RouteHandler = new RH();
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);

Switching to {controller}/{id}/{action} breaks RedirectToAction

I am trying to use proper REST urls with MVC. To do that I switched default Routing from:
{controller}/{action}/{id}
to
{controller}/{id}/{action}
so instead of:
/Customer/Approve/23
there is now
/Customer/23/Approve
ActionLink seems to work ok, but the following code in CustomerController:
[CustomAuthorize]
[HttpGet]
public ActionResult Approve(int id)
{
_customerService.Approve(id);
return RedirectToAction("Search"); //Goes to bad url
}
ends up on url /Customer/23/Search. While it should be going to /Customer/Search. Somehow it remembers 23 (id).
Here is my routing code in global.cs
routes.MapRoute(
"AdminRoute", // Route name
"{controller}/{id}/{action}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { id = new IsIntegerConstraint() }
);
routes.MapRoute(
"Default",
"{controller}/{action}",
new { controller = "Home", action = "Index" });
If I switch the two functions, RedirectToAction starts working, but using:
Html.ActionLink("Approve", "Approve", new { Id = 23})
Now generates /Customer/Approve?id=23, instead of /Customer/23/Approve.
I could specify direct urls like ~/Customer/23/Approve, instead of using ActionLink and RedirectToAction, but would rather stick to functions provided by MVC.
When you use RedirectToAction(), internally, MVC will take the existing route data (including the Id value) to build the url. Even if you pass a null RouteValueDictionary, the existing route data will be merged with the new empty route value data.
The only way around this I can see is to use RedirectToRoute(), as follows:
return RedirectToRoute("Default", new { controller = "Customer", action = "Search"});
counsellorben
Try passing in new (empty) RouteValueDictionary in your controller
return RedirectToAction("Search", new System.Web.Routing.RouteValueDictionary{});
And here:
Html.ActionLink("Approve", "Approve", new { Id = 23})
I don't even know how can it pick up the Customer controller, since you are not specifying it anywhere. Try providing both controller and action to ActionLink helper.
Try passing the current route data to methon in your controller action:
return RedirectToAction("Search", this.RouteData.Values);
Remove this part:
id = UrlParameter.Optional
may be resolve the problem; when you define "id" as an optional parameter, and you have the "Default" map, the "Default" and the "AdminRoute" are same together!
regards.
I was having a similar problem. Route values that were passed to my controller action were being reused when I tried to redirect the user with RedirectToAction, even if I didn't specify them in the new RouteValueDictionary. The solution that I came up with (after reading
counsellorben's post) with was to clear out the RouteData for the current request. That way, I could stop MVC from merging route values that I didn't specify.
So, in your situation maybe you could do something like this:
[CustomAuthorize]
[HttpGet]
public ActionResult Approve(int id)
{
_customerService.Approve(id);
this.RouteData.Values.Clear(); //clear out current route values
return RedirectToAction("Search"); //Goes to bad url
}
I had a similar problem and was able to solve it by adding the id to the default route as well.
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
If there is truly no id in your default route then you could also try:
routes.MapRoute(
"Default",
"{controller}/{action}",
new { controller = "Home", action = "Index", id = string.Empty });

ASP.NET MVC twitter/myspace style routing

This is my first post after being a long-time lurker - so please be gentle :-)
I have a website similar to twitter, in that people can sign up and choose a 'friendly url', so on my site they would have something like:
mydomain.com/benjones
I also have root level static pages such as:
mydomain.com/about
and of course my homepage:
mydomain.com/
I'm new to ASP.NET MVC 2 (in fact I just started today) and I've set up the following routes to try and achieve the above.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("content/{*pathInfo}");
routes.IgnoreRoute("images/{*pathInfo}");
routes.MapRoute("About", "about",
new { controller = "Common", action = "About" }
);
// User profile sits at root level so check for this before displaying the homepage
routes.MapRoute("UserProfile", "{url}",
new { controller = "User", action = "Profile", url = "" }
);
routes.MapRoute("Home", "",
new { controller = "Home", action = "Index", id = "" }
);
}
For the most part this works fine, however, my homepage is not being triggered! Essentially, when you browser to mydomain.com, it seems to trigger the User Profile route with an empty {url} parameter and so the homepage is never reached! Any ideas on how I can show the homepage?
Know this question was asked a while back but I was just looking to do the same sort of thing and couldn't find any answer that quite solved it for me so I figured I'd add my 2 cents for others that may also look to do the same thing in future.
The problem with the proposed solution above (as mentioned in Astrofaes' comment) is that you would need to create static routes for every controller in your assembly. So in the end I ended up using a custom route constraint to check whether or not a controller exists in the executing assembly that could handle the request. If there is then return a false on the match so that the request will be handled by another route.
public class NotControllerConstraint : IRouteConstraint
{
private static readonly IEnumerable<Type> Controllers = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.BaseType == typeof(Controller));
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
return Controllers.Where(c => c.Name == values["id"] + "Controller").Count() == 0;
}
}
Routes can then be set up as follows:
routes.MapRoute("User", "{id}", new { controller = "User", action = "Index" }, new { notController = new NotControllerConstraint() });
routes.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional });
Can you not swap the bottom two routes?
The reason that swapping the routes works, is because the {url} route doesn't have a constraint on it against empty strings (which is what your last route is). As a result, it will match the empty string first as it's higher in the route table.
With that in mind, you can either add constraints or add your specifically named routes higher in the routes table, or use the default catch all routes that mvc gives you to start with.
If you want to know which routes are matching at any given moment, then you can use the Route Debugger from Phil Haack.
I was looking to implement same style of url for my MVC 1.0 application.
I have implemented that with information from this post and blog post by Guy Burstein.
Thanks for sharing :)
I have a simlar setup as below:
routes.MapRoute(
"Common",
"common/{action}/{id}",
new { controller = "common", action = "Index", id = "" }
);
routes.MapRoute(
"Home",
"",
new { controller = "Home", action = "Index", id = "" }
);
routes.MapRoute(
"Dynamic",
"{id}",
new { controller = "dynamic", action = "Index", id = "" }
);
This allows me to be flexible and have the routes
mysite.com/
mysite.com/common/contact/
mysite.com/common/about/
mysite.com/common/{wildcard}/
mysite.com/{anything}

Resources