Legacy URL rewriting with query string parameters - asp.net-mvc

I've looked at ASP.Net MVC routing legacy URLs passing querystring Ids to controller actions and several other similar posts for legacy URL routing, but I can't get past the error "The RouteData must contain an item named 'controller' with a non-empty string value." Looking this up on line didn't give me any hints to solve my problem.
I've implemented the Legacy routing class described in the link above, and this is what I've defined in the routing table:
routes.Add(
"Legacy",
new LegacyRoute("fooref.aspx",
"FooRef",
new LegacyRouteHandler())
);
routes.MapRoute(
"FooRef",
"{controller}/{action}",
new
{
controller = "Home",
action = "Index",
foo_id = UrlParameter.Optional,
bar_id = UrlParameter.Optional
}
);
When I use Phil Haack's route debugger, it indicates that fooref.aspx has a match, but when I turn the route debugger off, I get the error above. If I reverse the statement order, I get "Resource not found" for /ctprefer.aspx, which makes sense -- so it appears to be finding that as a valid route when put in the other order.
Where do I need to declare this missing controller reference?
Have routing requirements changed for ASP.NET MVC 2 RTM?

The solution to this is to use an IHttpHandler directly, rather than an MVCHandler. I have posted code that works with MVC 3 to my blog: http://www.olsonsoft.com/blogs/stefanolson/post/Handling-Legacy-Urls-in-AspNet-MVC-3.aspx
...Stefan

You might want to take a look at URL Rewrite module for IIS. It can be used to translate legacy URL to your new MVC URLs without 'polluting' your app with legacy routes.
Don't know if it will fit your solution but it's worth having an alternative.

Related

Reserved ASP.NET MVC Controller Names?

So I just spent what felt like an hour debugging why I was receiving a client-side 404 and a server-side System.Web.Http.HttpResponseException on System.Web.Http.dll!System.Web.Http.Dispatcher.DefaultHttpControllerSelector.SelectController(System.Net.Http.HttpRequestMessage request) + 0x33f byte; no matter what I tried I could not get my ApiController derivative to receive requests.
In the end, after trying to convert it to a regular Controller to no effect, it occurred to me to change the name from APIController to APIXController only to have everything work.
Where is it stated that API is a reserved controller name?
Where is the full list of reserved controller names?
Is it possible to bypass this restriction on using APIController as my class name, and if so, how do I go about doing so?
One of the first things I checked was my route configuration, which is the bog standard
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
APIController is not a reserved name. But, if you're using a standard MVC4 template, then by default it includes an instance of a WebAPI stack, and this registers a route that starts with /api
You were most likely running into that.
Look under AppStart and in the WebApiConfig.cs file.
By the way, the key debugging symptom is anything in System.Web.Http, that is used by WebApi, and would not show up in an MVC app.

routing with just one of two parameters of controller

I have an actionresult with two parameter:
public ActionResult Index(int a,string b)
{
//some code
return View(b);
}
it creates this url automatically:
mysite.com/a=1&b=http://site.com/b=1
I just need to show first parameter "a" in my url:
mysite.com/a=1
I use the default route of MVC that creates in global.ascx:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
what should i do?
Thanks...
If you are seeing the "b" parameter bleed through from the current request, you can set the "b" parameter explicitly to empty string to avoid this behavior:
#Html.ActionLink("Home", "Index", "Home", new { a = 1, b = "" }, null)
I reported this "feature" as a bug, but the developers at Microsoft seem to think this behavior is supposed to make your URLs easier to configure, and they don't intend to fix it.
What you're seeing here is a feature of routing where "ambient" values (i.e. values that were detected in the incoming request's URL) are used to simplify (sometimes...) the generation of routes to other pages.
You can check our my answer (under the name "Eilon") in this StackOverflow post, where I explain the behavior in a bit more detail:
How Can I Stop ASP.Net MVC Html.ActionLink From Using Existing Route Values?
Ultimately if you want the most control over what gets generated for a URL there are a few options to consider:
Use named routes to ensure that only the route you want will get used to generate the URL (this is often a good practice, though it won't help in this particular scenario)
Specify all route parameters explicitly - even the values that you want to be empty. That is one way to solve this particular problem.
Instead of using Routing to generate the URLs, you can use Razor's ~/ syntax or call Url.Content("~/someurl") to ensure that no extra (or unexpected) processing will happen to the URL you're trying to generate.
Thanks,
Eilon

How route for generation outgoing URL is chosen in ASP.NET MVC?

Good day!
I'm using ASP.NET MVC 2 and T4MVC and it seems some code magic is happening
When I add this to routes table:
routes.MapRoute(
"Login",
"login/",
MVC.Profile.Login()
);
How does framework know that I want this rule to apply when I write something like this in the view to generate outgoing URL:
<%: Url.Action(MVC.Profile.Login() %>
What if I have multiple different rules (with different params) for the same controller/action pair? Which one will be chosen? Is there anywhere a good description of this behavior?
Thanks in advance!
What I would suggest to help you understand how this work is to separate the magic that T4MVC does from what MVC itself does under the cover.
When you write this with T4MVC:
routes.MapRoute(
"Login",
"login/",
MVC.Profile.Login()
);
It's equivalent to writing this with straight MVC:
routes.MapRoute(
"Login",
"login/",
new { controller = "Profile", action = "Login" }
);
And in the view:
Url.Action(MVC.Profile.Login())
Is the same as
Url.Action("Login", "Profile")
T4MVC gives you the benefit of strong typing/intellisense, but in the end what it does is the same as with straight MVC.
Hopefully this helps clear things up a bit :)
It matches the route patterns in the order you define them. Thats why you have the default pattern as the last one. As soon as it finds a matching pattern, it stops looking.
Edit
Parameters are ignored during route matching. Once a controller method has been selected, mvc uses model binding to assign the parameters to the method variables.
If you can explain what type of url structure you are looking to use, we can probably help you more.
Your example is not valid MVC, you would normally pass the controller name, action and any other parameters, then the routing engine would use all that information to determine which route to use, the more routes you have defined, the more information it will probably require to determine the one YOU want to match

ASP.NET MVC Routing Questions

I just started using ASP.NET MVC and I have two routing questions.
How do I set up the following routes
in ASP.NET MVC?
domain.com/about-us/
domain.com/contact-us/
domain.com/staff-bios/
I don't want to have a controller specified in the actual url in order to keep the urls shorter. If the urls looked liked this:
domain.com/company/about-us/
domain.com/company/contact-us/
domain.com/company/staff-bios/
it would make more sense to me as I can add a CompanyController and have ActionResults setup for about-us, contact-us, staff-bios and return appropriate views. What am I missing?
What purpose does the name "Default" name have in the default routing rule in Global.asax? Is it used for anything?
Thank you!
I'll answer your second question first - the "Default" is just a name for the route. This can be used if you ever need to refer to a route by name, such as when you want to do URL generation from a route.
Now, for the URLs that you want to set up, you can bypass the controller parameter as long as you're ok with always specifying the same controller as a default. The route might simply look like this:
{action}/{page}
Make sure that it's declared after your other routes, because this will match a lot of URLs that you don't intend to, so you want the other routes to have a crack at it first. Set it up like so:
routes.MapRoute(null, "{action}/{page}",
new { controller = "CompanyController", action = "Company", page = "contact-us" } );
Of course your action method "Company" in your MyDefault controller would need to have a "string page" parameter, but this should do the trick for you. Your Company method would simply check to see if the View existed for whatever the page parameter was, return a 404 if it didn't, or return the View if it did.
Speaking of setting up routes and Phil Haack, I have found his Route Debugger to be invaluable. It's a great tool for when you don't understand why particular routes are being used in place of others or learning how to set up special routing scenarios (such as the one you've mentioned). It's helped clear up many of the intricacies of route creation for me more than any other resource.
To answer your second question about Global.asax, it an optional file used for responding to application level and session-level events raised by ASP.NET or HTTP modules. The Global.asax file resides in the root directory of the ASP.NET application.If you do not define it assumes you have not defined any application handler or session handler. MVC framework uses the routing engine to which the routing rules are defined in the engine, so as to map incoming URL to the correct controller.
From the controller, you can access the ActionName. If there is no specific controller, it will direct to the default page. The default controller is "Home" with its default action "Index". Refer to MSDN:
http://msdn.microsoft.com/en-us/library/2027ewzw%28v=vs.100%29.aspx
Refer to stackoverflow question
What is global.asax used for?
This is a sample of how a default route should look like
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id =
UrlParameter.Optional }
);

ASP.NET MVC Default route?

I created a new ASP.NET MVC project and implemented a site authorization filter.
When I map the routes to the {controller}/{action} pair, I pass a role = "SomeRole" default to the route.
It works perfectly if I go through the full url (http://localhost/somecontroller/someaction) and I specified the full route
MapRoute("SomeAction", "somecontroller/someaction",
new { controller = "SomeController", action = "SomeAction", role = "SomeRole");
The problem is that when somebody visits http://thesiteaddress.com there has to be a default route that invokes /home/index instead of / and if I specify
MapRoute("Default", new { controller="somecontroller",action="action" });
then I lose the role="SomeRole" from the previous MapRoute.
How can I solve this?
Make sure the Default route is at the BOTTOM of your listed route table. Order matters when it comes to ASP.NET MVC Routing tables.
The correct ordering is your 'most specific' route to your least specific route.
Actually, George is right. MVC Routing respect ordering route. Your last route must be generic as possible, and your previous route must be specific as possible.
In your case, both are generic. You should
MapRoute("SomeAction", "Post/{action}", new {controller = "Post", role = "User");
and then
MapRoute("Default", new {controller="Home", action="Index", role = "Anonymous"});
so, you give specificity to both routes.
Phil Haack released a route debugging tool that can be invaluable in gaining an understanding of problems like this.
With this tool you can view how your MVC application parses a URL and matches it to your RouteTable.
When you don't provide the route name or the action is determined through a HTTP request it will look in order from the order they were added. The first time it finds one that matches, it stops. So what's probably happening is it's matching one previous to the one you've added.

Resources