We have an MVC application that is accessed from two separate websites. The default website is fine, however how can I setup the second site to startup in a specific Controller?
Our default site is www.mysite.com, and we'd like to add a second IIS site for the header www.subdomain.mysite.com that should take users to www.subdomain.mysite.com/controller
But how can I tell IIS to startup www.mysubdomain.mysite.com with the specific controller action mycontroller?
I would consider configuring URL Rewrite so that requests to www.mysubdomain.mysite.com get seen as requests to www.mysubdomain.mysite.com/controller.
You could use Ionics Isapi Rewrite Filter.
Ionic's Isapi Rewrite Filter, aka IIRF, is a small, FREE, easy to use,
URL rewriting ISAPI filter. It combines a good price (free!) with good
features. It is fast and powerful. It works on IIS 6.0, and later.
I ended up adding a value to my AppSettings in web.config, and adjusting the default route of the application based on that value.
public static void RegisterRoutes(RouteCollection routes)
{
var defaultController = ConfigurationManager.AppSettings["DefaultController"];
if (string.IsNullOrEmpty(defaultController))
defaultController = "Home";
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = defaultController, action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
...
}
This allows me to host any number of IIS sites that can each start with a different controller in the application.
Related
My question up front
How do I construct a route so that MVC will intercept the classic ASP URL and instead execute an MVC action?
I am migrating a legacy classic ASP application to MVC, and need to have MVC intercept a couple of the legacy ASP URLs because they are major endpoints for external access to the application. But I can't seem to figure out how to do it correctly.
I checked a few other questions and didn't quite find what I'm looking for, but maybe my search-fu is poor today. This one is specific to areas but looks similar to mine which still doesn't work, and this one is a possible workaround but I'd really rather handle this completely within MVC and eliminate the legacy file completely.
What I want to do
Given: /foo/bar.asp
Map to: /InboundLinks/HandleBar
(one URL will be a GET request, but the other will be a POST with some sensitive data, so I need them to be intercepted and the POST data still available to MVC, not sure if a 301 redirect will do that or not)
What I DON'T want to do
I do NOT want to run the classic ASP pages at all. (I'm willing to have it solely do a 301 redirect to the MVC URL if that is the only workaround, but that's it) I want the URLs to be intercepted and handled by MVC. I say this because a few questions I found here and elsewhere seemed to generate some confusion on that point.
What I've already tried
routes.MapRoute(
name: "LegacyBarUrl",
url: "foo/bar.asp",
defaults: new { controller = "InboundLinks", action = "HandleBar" }
);
But this returns a 404 Not Found error.
Environment
Visual Studio 2013 running in local dev mode on Windows 7. Deployment will be to IIS 7 on a locked down server I don't control, so installing HTTP modules on the server isn't an option unfortunately. The domain will remain the same.
Many thanks in advance for any help/guidance/etc.
What you've tried must work. Make sure it comes at the top of your routing configuration, and the default route comes after it.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "LegacyBarUrl",
url: "foo/bar.asp",
defaults: new { controller = "InboundLinks", action = "HandleBar" }
namespaces: new[] { "YourProject.Controllers" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "YourProject.Controllers" }
);
}
EDIT:
OK, I've tried this myself as well and it really does not work. So you have two options:
(1) capture and route your request at the IIS level: If you take this path, this extension might be very helpful: http://www.iis.net/downloads/microsoft/url-rewrite]
(2) write your own RouteBase and redirect legacy routes before MVC looks up the routing table: If you take this path, this article would be very helpful to you (it would be too long to write the code here): http://www.mikesdotnetting.com/article/108/handling-legacy-urls-with-asp-net-mvc
To Anyone running into this problem, the 404 is because the server is looking for the physical file before getting into the routes. What is needed is a handler for the extension, in this case for the classic asp that is going to catch the request so the server doesn't look for the file anymore, and the request is handled by your handler.
Add an entry to the web config file in the handlers section like this:
<add name="ClassicASPHandler" path="*.asp" verb="GET" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
Then add the needed route to the configuration routes table. Now, the route will be handled correctly as opposed of looking for the file and returning a 404.
routes.MapRoute(
name: "LegacyBarUrl",
url: "foo/bar.asp",
defaults: new { controller = "InboundLinks", action = "HandleBar" }
namespaces: new[] { "YourProject.Controllers" }
);
I've added a custom 404 page to my asp.net mvc application. It works totally great except for on some paths that have been excluded from the MVC routing engine. As you can imagine, I'd like my 404 page to work for those URLs as well.
So the question is: Can I add some setting in IIS I can use to just point it to the 404 page's endpoint.
Thanks!
No, there isn't any custom setting on IIS.
Works for me. I have the following settings set in web.confg
My RegisterRoutes method is as below
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 = UrlParameter.Optional } // Parameter defaults
);
}
I ended up writing a StaticContentController that simply opens a file stream and returns it to the browser. The path is controlled tightly by the routing engine to avoid security issues, and it is heavily cached.
A note on the file stream, when I first implemented it, I was using MVC's return File(... method, but that locks the file while it is streaming it, so I had some instances of file contention for files that were written to and served concurrently. That's why I switched to returning a FileStream, so I could pass the correct file sharing options that allow for the file to be written to by another process.
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.
Recently, I partially converted an Asp.Net web forms application to use MVC. We still have parts of the application in web forms (.aspx pages) and use MVC routing to work with Controllers and such.
I added an MVC route like
routes.MapRoute("Users", "Users/{controller}/{action}/", new { controller = "Timesheet", action = "List" });
There is a folder called "Users" which contain a few aspx pages we still use.
When I hit the URL http://localhost/Users/ I get a directory listing of the contents of the "Users" folder. Apparently, the directory listing takes precedence over MVC url routing and this might be overridden by modifying the IIS7 server settings.
How could I override this behavior, via code or web.config changes?
References:
http://forums.asp.net/t/1251156.aspx/1
http://learn.iis.net/page.aspx/121/iis-7-and-above-modules-overview/
Setting RouteExistingFiles=true on the RouteCollection achieves just that. It will allow ASP.NET MVC to handle routes even for existing directories.
Use this ignoreroute:
routes.IgnoreRoute("{WebPage}.aspx/{*pathInfo}");
Listing the RegisterRoutes method
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{WebPage}.aspx/{*pathInfo}");
//routes.MapPageRoute("users", "users", "~/admin/default.aspx");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "home", action = "index", id = UrlParameter.Optional } // Parameter defaults
);
}
This would exclude all pages whose extension is ".aspx" from routing.
I have deployed my application to a server running IIS6 using the method which invloves changing the routes to:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}.mvc/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
routes.MapRoute(
"Root",
"",
new { controller = "Home", action = "Index", id = "" }
);
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
}
And adding a handler in IIS for .mvc extentions. This is working fine for the most part until I add the [Authorize] attribute to HomeController class.
This ends up in the app trying to redirect the user to the logon page which is what I expect however the logon page URL is shown as http://server/virtualdir/Account/LogOn?ReturnUrl=%2fvirtualdir%2fDefault.aspx
This is causing a problem as no .mvc extension is being added to the Account controller part of the URL.
The problem has been solved by changing the following in web.config:
<authentication mode="Forms">
<forms loginUrl="~/Account.mvc/LogOn" timeout="2880" />
</authentication>
Not directly an answer to your question, but from my experience it worked just fine to deploy an application with the new routing features just as it is to IIS6 and add a wildcard mapping to aspnet_isapi.dll. Then you can use any URL you want, and nobody will notice when you change to a newer version in the future.
Yes, static file handling is theoretically less efficient this way, but you will need really a lot of traffic to notice anything. And if you really get a lot of traffic, you still could and even should move all your static files to a another domain/subdomain (or even a CDN) anyway, like stackoverflow.com does. It can still point to the same server, you just use different IIS settings for this subdomain site. But with e. g. just a few thousand visitors per day you don't even have to think about it.