404 after setting non-default ASP.net MVC routes - asp.net

I'm using ASP.net MVC4 with VWD Express 2010. I've set up an extra route to remove the extraneous /Home/ from URLs, as suggested for example here and here. I made sure to put them in the right order:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Home",
url: "{action}",
defaults: new { controller = "Home", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
This works as far as removing /Home/ is concerned, but now I get 404 when trying to access non-default controllers:
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /LogItem/
I've tracked it down to the route, since if I remove the non-default one, it works fine again. I'm not sure what I'm doing wrong however, and by my understanding of other answers and the docs, this should work.
Edit: Since it's apparently not clear from my description, I added the "Home" route to be able to link to /About/ instead of /Home/About, etc. It was the suggested solution I found after googling. I basically want it to match actions implemented by HomeController with shorthand URLs (/{action}), as well as any other controller I also add in full (/{controller}/{action}/{optional id})

Routes are evaluated on a first-come, first-served basis. Your "Home" route basically says "If you see a URL with just one segment - grab it and handle it, then assume that segment is the action. When that happens, the second route you've registered doesn't get a chance to handle the request.
With the specific error as an example, you are requesting "/LogItem". The Home route kicks in, parses "LogItem" as the name of the action, and passes it to the default controller. Since that controller maybe doesn't have a LogIn action, a 404 is thrown.
One solution is to change the order of the routes. But even then, those two are overlapping, since the 2nd will also handle URLs with 1 segment, but interpret that segment as the controller. It will execute the default action (Index) rather than passing the handling to the next route.
Why do you need the first route? What are you trying to achieve? If you provide more context we can figure out a way to implement those routes.

Related

How to use a WebAPI Catch all but allow root and help URIs

I have an asp.net WebApi REST service where I would like to format my own message for a 404 (ie route not found). I am using attribute routing.
All of my controller routes are nested under v1/. Also, for just the root url, I display a pain html page (ie I provide a default.htm file), an also under the /help I show some help (via mvc help pages). So, I don't want the 'catch all' route to intercept these urls.
I found this post, which seemed to work, even in conjunction with the attribute routing, except it also does trap my root page.
I tried the following
httpConfig.Routes.MapHttpRoute(
name: "ResourceNotFound",
routeTemplate: "{uri}/{*v1}",
defaults: new { controller = "ResourceNotFound", uri = RouteParameter.Optional });
ie added the {*v1}, however this does not work either. I get the following (my catch all controller is named ResourceNotFoundController)
<Error>
<Message>
No HTTP resource was found that matches the request URI 'http://localhost/myapi/'.
</Message>
<MessageDetail>
No action was found on the controller 'ResourceNotFound' that matches the request.
</MessageDetail>
</Error>
I have tried various variations of routeTemplate patterns, but can't get it to work where it allows the root page through.
Does anyone have any ideas on how I can do this?
Thanks in advance for any help!

Disable all server routing except for api calls in asp.net

I have created a angular2 project using ASP.net with webapi. I have everything setup correctly and working but when I am routing in angular2 such as: "localhost:1234/login" the source of the page is displaying the error:
HTTP Error 404.0 - Not Found
This is obviously because of server routing that cant find any path for this. My question is: Is there any way to disable all routing by the server except for if I'm doing a call like: "localhost:1234/api/login" because that would entail making a server request to a webapi controller which will return JSON. I want angular2 to handle all forms of routing. And the server just to handle data being pushed out.
Thank you in advance
If Angular is generating views by consuming APIs this should not be an issue. If the server is expected to serve up HTML for the routes Angular is calling then it needs equivalent routing configured.
Yes you can do this in your startup.cs class.
You need to configure the way your API calls are made in a way, and redirect to your index.html otherwise like so :
app.UseMvc(r =>
{
r.MapRoute(
name: "default",
template: "{controller}/{path?}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" }
);
r.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" }
);
});

Convention-based routing in Symfony2

I'm trying to learn learn how routing works in Symfony2, and so far everything I've read has examples like this:
blog:
path: /blog/{page}
defaults: { _controller: AcmeBlogBundle:Blog:index, page: 1 }
This routes requests to /blog/123 to the AcmeBlogBundle Blog controller's "index" action, and passes the 123 parameter as the "page" parameter to that controller action. If no page parameter is passed, then the page defaults to 1.
That's all well and good, but what if you want to simply have a convention based routing system that passes things through like this:
/{bundle}/{controller}/{action}
So, for a URL like this:
/acme/blog/index
It would then call AcmeBlogBundle Blog controller's "index" action.
No specific routing configuration is necessary, it simply infers the bundle, controller, and action from the URL. So you can continue adding bundles, controllers, and actions, and you don't need to modify the routing configuration. It just works.
If this isn't possible, can you at least infer the controller and action from the URL? E.g., perhaps you need a route that specifically identifies the bundle, but can we get the controller and action from the URL?
I read through the Symfony "The Book" page about routing, and I couldn't figure out a way to do this.
No way. This was considered as bad practice and so it was removed from symfony.
But you should take a look at the #Route annotation, as it simplifies configuring routes in such a nice way. Directly attached to the action, there is no lack between config and code.

Html.RouteLink to a Web API Route - possible?

My site is largely a suite of web services exposed via the Asp.Net Web API. There are also pages, designed to support the webservices (testing etc), written in Razor (and implicitly Asp.Net MVC 4).
For the XML versions of the webservices I have a schema-export action (uses the XsdDataContractExporter) which is picked up by my standard API route (although note - I've flipped the precedence of the Web API and Pages):
//page routes
routes.MapRoute(
"Default", // Route name
"pages/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home",
action = "Index",
id = UrlParameter.Optional
} // Parameter defaults
);
//an additional route for my Schema controller action
routes.MapHttpRoute("XSD", "schema.xsd",
new { controller = "schema" });
//API Catch-all Route
routes.MapHttpRoute("APIMain", "{controller}/{id}",
new { id = RouteParameter.Optional });
Now on a razor page I want to emit a link to the 'friendly' schema URL ~/Schema.xsd. Anticipating issues with route discovery I immediately went for hitting the route directly by name:
#Html.RouteLink("Schema", "XSD");
However this just emits a link equivalent to ~/.
I've tried some other combinations of route values - but it appears MVC's HtmlHelper and UrlHelper simply don't want to pick up Web API routes.
I'm sure if I cracked open the source of Asp.Net MVC 4 I'd find the reason - but I'm hoping somebody already knows, and since I can't find another SO about such cross-linking I figured it'd be a good addition to the SO library.
I should add that browsing to ~/Schema and ~/Schema.xsd do correctly display the XML schema produced by the API action.
Update
Post-RC a method was added to MVC's UrlHelper, HttpRouteUrl, which does exactly the same thing I suggest here in this answer. This is my discussion thread over on CodePlex where I was told this. So there is no need for you to use the magic string mention here in generating links to Web API routes.
Original answer
I've managed to get it to work - although it might not by the time MVC 4 is RTMd (disclaimer disclaimer!)
I changed my Html.RouteLink call as follows:
#Html.RouteLink("XML request schema", "XSD", new { httproute = true })
I didn't originally intend to answer my own question straight away - but having done some research I found an answer.
First I verified that the HtmlHelper's route collection is the same as the RouteTable.Routes collection (i.e. contained all routes).
Following the call-chain through, I remembered having trawled through the current Web API and page MVC 4 source code from CodePlex, that HttpRoutes (in System.Web.Http.Routing) need a 'hidden' route value to be added otherwise they will never match. Here's the source code from lines 21-25 of HttpRoute class (correct as of 8th June 2012 source):
/// <summary>
/// Key used to signify that a route URL generation request should include HTTP routes (e.g. Web API).
/// If this key is not specified then no HTTP routes will match.
/// </summary>
internal const string HttpRouteKey = "httproute";
A bit of further analysis of the code showed that it expects this route value to be a boolean.
Clearly, this is something that can be turned into extension methods - perhaps Html.HttpRouteLink (and Html.HttpActionLink) - with extra extensions on UrlHelper for hiding the magic string for the route data value.

Returning Requested URL within Custom Error Page in ASP.net

Working in ASP.net 3.5 and MVC 1.0.
What I would like to do is return the requested URL, which generates a 404 error, within the custom error page. Much like Google does on their error pages (http://www.google.com/test).
eg.
We're sorry, but the requested address "http://www.domain.com/nonexistantpage.aspx" does not exist on this server.
What would be the best way to accomplish this kind of soft 404?
Also, as a side note: Anyone familiar with returning the custom error page in place of the ugly ...notfound?aspxerrorpath=/awdawd nonsense, while keeping the requested URL in a browser's address bar? ...I suspect something to do with a server.transfer?
Check out these resources related to this topic:
ASP.Net MVC Custom Error Pages
Three common ASP.NET MVC URL routing issues
To summarize, you can accomplish a google-like implementation with keeping the requested URL by defining a catch-all route that executes a particular controller action.
//defined below all other routes
routes.MapRoute(
"Catch All",
"{*path}",
new { controller = "Error", action = "NotFound" }
);
public ActionResult NotFound(string path)
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
ViewData["path"] = path; //or Request.Url.ToString() if you want full url
return View();
}
This is not a complete solution, though. Assuming you've left the default route mapping, anything that matches {Controller}/{action}/{id} is still going to a throw a traditional 404 or custom error. You'd have to explicitly define all possible routes if you truly wanted to have the catch-all route pick up anything that didn't map to a specific controller/action or parameter type - not necessarily a trivial task.

Resources