Problem
I have two controllers: RequestsController and ServicesController. Both contain Index actions.
When I browse to /Requests, it automatically runs the Index action, but for /Services or /Services/, it gives an HTTP 404 without even running the Index action.
Background
The route configuration is stock. The project also contains various classes under a top-level directory called Services:
Troubleshooting
The problem seems to be related to some sort of clash in naming between ServicesController and the top-level Services folder.
I can still access /Services/Index without a problem.
Debugging confirms that the Index method is being run for Requests but not Services when I don't specify the action name in the URL.
Renaming or removing the top-level Services folder causes the problem to stop happening.
I see. That's a common issue when you have a controller with the same name as a folder inside the root directory. Assuming that none of the files inside the Services folder are mapped by the StaticFilesHandler I think you can simply tell the routing system to map all "services" routes to the ServicesController by setting the RouteExistingFiles to true...
routes.RouteExistingFiles = true;
Update
I completely ignore the Content folder. You will need to prevent requests to the "Content" folder (or any other folder containing static files such as the Scripts folder) from going through the routing pipeline by explicitly specifying it BEFORE your routing-mapping logic...
routes.IgnoreRoute("FOLDER_NAME/{*pathInfo}");
Related
I have an annoying problem with folder names and routes in ASP .NET Mvc 5,
This is the default routes I'm using:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Site", action = "Index", id = UrlParameter.Optional }
);
And I'm also using a custom view engine to map views to the root folder instead of ~/Views so I can have HTML/CSS/JS files organized in a way my team can handle.
The problem is: When I call /Backoffice/Index it goes normally and executes action Index in controller Backoffice, all fine, but when we call /Backoffice, I expected it to presume the action index (by the route configuration), but instead, IIS seems to believe I'm trying to access the folder /Backoffice and gives me a 404 error instead of executing Backoffice::Index().
How can I configure IIS to behave in the intended way in this case? Or, is it really the only best way to keep views in a specific folder?
I did some research and used info from the comments and answers here and got to learn something about how MVC routes work and how IIS handles that.
When we set
routes.RouteExistingFiles = true;
The MVC runtime will ignore every file in the folder and start using the routes and URIs solely to look for controllers and actions, this means by calling /SomeContent it will search for a controller named Somefolder instead of a folder. Then we can configure to serve static content by telling MVC runtime to ignore some specific URI formats:
routes.IgnoreRoute("SomeContent/CSS/{filename}.min.css");
routes.IgnoreRoute("SomeContent/JS/{filename}.min.js");
This causes MVC to ignore URIs that match this pattern and leave it for IIS to resolve what to do with it, then IIS will look out on Web.Config rather there are configurations set to serve this kind of static content and what handler to use and proceeed as usual.
Using this configuration can bring MVC to a whole new level of control where you explicitly define which URI patterns serve static content, everything else explicitly calls an action on a controller, all URIs get to be firstly processed by MVC runtime. I sure wish someone correct me in this last statement if I get it wrong.
MVC routes certain actions based on whether they exist on disk. If you have a folder /BackOffice at the root level, then this appears to be a complication that MVC is going to have issues working around (I knew files were directly routed if they existed; I didn't realize folders were something the framework checked too). Consider renaming the folder or the controller to something else so you don't have this naming conflict. That is a problem with "by convention" approaches....
I'm trying to determine if it is possible (or practical) to implement Uncle Bob's Screaming Architecture in ASP.NET MVC 5 rather than using the default folder structure.
Here's a link to a description of the Screaming Architecture: http://blog.8thlight.com/uncle-bob/2011/09/30/Screaming-Architecture.html
A hypothetical folder structure would look something like this:
Root
Customers
Controllers
CustomerController.cs
Models
Customer.cs
Views
Index.cshtml
Details.cshtml
Update.cshtml
Employees
Controllers
EmployeesController.cs
Models
Employee.cs
Views
Index.cshtml
Details.cshtml
Update.cshtml
Shared
Views
_Layout.cshtml
Error.cshtml
_ViewStart.cshtml
Web.config
The corresponding URL routes would look like this:
http://www.example.com/customers/ => Customer Index
http://www.example.com/customers/details/1 => Customer Details
http://www.example.com/customers/update/1 => Customer Update
http://www.example.com/employees/ => Employee Index
http://www.example.com/employees/details/1 => Employee Details
http://www.example.com/employees/update/1 => Employee Update
I've created a custom RazorViewEngine and added the appropriate view location formats (e.g. "~/{1}/Views/{0}.cshtml") and partial view location formats (e.g. "~/Shared/Views/{0}.cshtml"). I've also moved the shared _ViewStart.cshtml to the root and merged the Views/Shared folder's web.config with the root-level web.config to avoid having to duplicate these two files in all of the View folders.
Everything works great, however, if I try navigating to an index page (e.g. http://www.example.com/employees/) I get a 403.14 Error (Forbidden). All other routes (including http://www.example.com/employees/index) work just fine.
My guess is that IIS is explicitly blocking the route to the controller's index method because the URL coincides with a folder in the filesystem and directory browsing is disabled by default. If I enable directory browsing, however, it actually takes me to the actual directory listing rather than routing to the controller's index method.
I can move the Customers and Employees folders into a subfolder (i.e. move them out of the root) and everything works fine, but I'd like to try to keep these folders at the top level (per the Screaming Architecture guidelines).
Does anyone have a solution for this issue?
Please note that MVC Areas is not the solution I'm looking for as it does not allow for the folder structure described above (i.e. top-level folders named after high-level use cases and views contained directly within the Views folder rather than in a subfolder).
I'm betting you are right about IIS then. If you have two paths mapped to the same resource, the physical path is checked first on the IIS side.
I was digging around the routes configuration and found the property RouteExistingFiles on RouteCollection and think this could work.
I set the value to true and tested locally with an empty folder in the project, a route redirecting to Home/Index, and navigating to localhost:xxx/MyFolder. It worked correctly.
So then all you should need to do is set this property to true for it to choose Asp.net routes first instead of physical routes.
Existing ASP.NET (MVC and webforms hybrid) website displays translated content. The language is based on a cookie that stores the user's preference. There is no change in the URL when the user changes the setting. The content is reloaded in the preferred language. For SEO, the locale should be included in the URL ( support.google.com/webmasters/answer/182192?hl=en).
I've tried the following:
1) Use URL Rewrite Module: (http://www.iis.net/learn/extensions/url-rewrite-module/setting-http-request-headers-and-iis-server-variables)
Issues:
- All hyperlinks and redirects still point to the old URL without the locale.
- Complex outbound rules required based on the folder structure and usage (mixture of absolute paths and relative paths e.g. ../, ~/, /).
- Also need to disable static compression as per documentation
- Performance considerations due to large size of Html.
- Postback results in runtime exceptions due to issue in the relative path rewrite.
- Paths defined in script files (ajax loading etc) are a huge challenge
- Base tag does not work as expected, because the Rewrite Module seems to append ../ (http://www.iis.net/learn/extensions/url-rewrite-module/url-rewriting-for-aspnet-web-forms#Using_tilda)
2) IIS 7.5 Virtual Directory: Create Virtual Directory for each language and point it to the root. i.e. www.example.com is the root and www.example.com/fr-ca/ is a virtual directory mapped back to the root
Issues:
- Runtime exception in config file saying that the virtual directory needs to be converted to application
- Converting it to application gives 500.19 error due to duplicate entries in the web config (since the virtual directory is pointing back to the root)
- I tried moving the root to another subdirectory (i.e. have a physical directory for each language) to avoid web config conflicts, but that is resulting in some sort of "kernel" error. Also, this would mean changing the physical structure of the application, and also address routing issues
3) Using sub-domains:
I have also considered using sub-domains and hosting the application independently for each language, but this has a lot of drawbacks, including having to address scalability, single sign on, cookies, domain specific stuff like analytics etc.
So what is the least painful way to include a language sub-directory in the URL, and make all links relative to that sub-directory?
Note: The site contains a mixture of absolute paths and relative paths e.g. (../, ~/, /) sometimes used in conjunction with ResolveClientUrl, ResolveUrl
In the end, we went with option 2, with the below steps:
Create a new folder, deploy a copy of the application to the new folder. The new folder should be in a different directory from the root application.
Create a new virtual application* (not virtual directory) under the root application; 1 for each new language, pointing to the new folder. (If the need arises in the future, any of the virtual applications can point a different folder customized for that specific language)
In the new folder, remove the modules and handlers sections in the system.webServer section of the web.config file (they will be inherited from the parent web.config)
If you are using SQL session state, you will need to specify a custom Application Name in the web.config, and modify TempGetAppID stored procedure so that the Application Name is the same across all the virtual applications. See the following (http://blogs.msdn.com/b/toddca/archive/2007/01/25/sharing-asp-net-session-state-across-applications.aspx)
Hopefully, all the links are resolved on the server side using Url.Content (MVC) or ResolveUrl (webforms). If not, they need to be fixed. Any paths specified in javascript would not automatically resolve to the virtual application either (they would still be resolved to root application)
Test the heck out of it. Each and every link. (A tool like ScreamingFrog may help to make sure that no 404s are returned, methinks. But it wouldn't solve HTTP POST)
Note that depending on custom error handling, and any existing URL rewrite rules, the steps maybe different.
Summary: option 1 (URL Rewrite) is totally impractical. Option 2 (sub-directory) is the most practical solution, however it is not quite as straightforward as it should've been.
I have an ASP.NET 4.5 application running at the root level of my localhost in IIS 7.5, within which, I have a route defined as archives:
http://localhost/archives
I'm trying to add a WordPress blog at archives/blog but can't figure out how to do so. So the URL for the blog should be as follows:
http://localhost/archives/blog
I tried placing an empty directory within the web application that runs at root level called archives, but as soon as I do that, I get an HTTP 403 error regarding directory browsing due to a conflict with the route.
Any idea how I can override the empty directory with the route? Or any other ways I can make the blog to work at the directory level where I want it to?
I've read about RouteExistingFiles property, but also hear that once it is set to true, I will also have to define routes for all my static content (CSS, JavaScripts, Images etc).
Thoughts?
I want to know the root of the application both system eg. /var/www/app/ or /app/ for the purposes of file uploads. When I upload, I believe I need the "system" directory name, for use to links in web front end, I need the "relative" path. How do I get this information?
In a previous project, I defined constants in the app*.php files (front controller)
define('APP_ROOT', __DIR__);
Wonder if theres a better way?
In any ContainerAware class you can get location of web directory by,
$this->container->get('kernel')->getRootdir().'/../web';
If the container is not available, you can use dependency injection: set %kernel.root_dir% as an argument to your class in the service configuration.