How to disable or reprioritize IIS DirectoryListin

2020-02-12 08:40发布

I use an MVC folder structure where the URL routes happen to match the directory names, eg.:

<proj>\My\Cool\Thing\ThingController.cs

Needs to be accessible by this url:

http://blahblah/My/Cool/Thing

I have the MVC routing working but unfortunately when relying on default {action} & {id}, IIS Express is routing the request to DirectoryListingModule instead, since it directly matches a folder name. Directory listing is disabled of course so instead I get:

The Web server is configured to not list the contents of this directory.
Module     DirectoryListingModule
Notification       ExecuteRequestHandler
Handler    StaticFile

To fix this I have already tried:

1. runAllManagedModulesForAllRequests = true

<system.webServer>
<modules runAllManagedModulesForAllRequests="true" >   
//Makes no difference

2. Removing module

<system.webServer>
  <modules runAllManagedModulesForAllRequests="true" >
    <remove name="DirectoryListingModule"/>   
    // Won't let me as module is locked in IIS
  </modules>
</system.webServer>

3. Removing lock & module

// applicationhost.config
<add name="DirectoryListingModule" lockItem="false" />

// web.config
<remove name="DirectoryListingModule"/>
// Causes startup error"Handler "StaticFile" has a bad module "DirectoryListingModule" in its module list"


4. Removing lock & removing/readding module (to change order) - makes no difference

// web.config
<remove name="DirectoryListingModule"/>
<add name="DirectoryListingModule"/>

Tearing my hair out. How can I get IIS to route this to my MVC app instead of DirectoryListingModue?? Preferably a solution in web.config so we don't need to reconfigure IIS in production.

(One workaround is to keep my folder structure but store it all under /Areas/... instead, just to break the match between folder path & url. This is a terrible hack & last resort.)

edit to add route mapping

I am creating custom routes relative to each controller's namespaces (namespaces always match folders). Note that everything is put under the "Modules" namespace / folder currently just to avoid the problem described above.

    private static void RegisterAllControllers(RouteCollection routes)
    {
        const string controllerSuffix = "Controller";
        const string namespacePrefix = "My.Cool.Websire.UI.Modules.";

        var controllerTypes = Assembly.GetExecutingAssembly().GetTypes().Where(x => x.IsSubclassOf(typeof(Controller))).ToList();

        foreach (var controllerType in controllerTypes)
        {
            // Turn My.Cool.Website.UI.Modules.X.Y.Z.Abc.AbcController into a route for url /X/Y/Z/Abc/{action}/{id}
            var fullNamespace = controllerType.Namespace ?? "";

            var relativeNamespace = fullNamespace.Substring(namespacePrefix.Length, fullNamespace.Length - namespacePrefix.Length);

            var controllerName =
                controllerType.Name.EndsWith(controllerSuffix)
                ? controllerType.Name.Substring(0, controllerType.Name.Length - controllerSuffix.Length)
                : controllerType.Name;

            var url = relativeNamespace.Replace(".", "/") + "/{action}/{id}";

            var routeName = "Dedicated " + controllerName + " route";

            routes.MapRoute(routeName, url, new { controller = controllerName, action = "Index", id = UrlParameter.Optional });
        }
    }

3条回答
Fickle 薄情
2楼-- · 2020-02-12 09:22

My solution at this stage is to place the MVC contents of the WebUI project under a /Modules/ folder:

My.Cool.Site.WebUI/Modules/Something/Blah/BlahController
My.Cool.Site.WebUI/Modules/Something/Blah/Views/...
My.Cool.Site.WebUI/Modules/Something/Blah/PartialViews/...

Then using the route code posted I can access this via url:

http://.../Something/Blah/[action]

Because the files are under /Modules/ folder, this breaks the match between the URL and the folder path which gets around my problem.

Not a great solution but does the job.

查看更多
在下西门庆
3楼-- · 2020-02-12 09:26

I've noticed that if I have a custom http module, pointed to from web.config like

    <modules>
        <remove name="WebDAVModule" />
        <add name="CustomHttpModule" type="MyCompany.Types.CustomHttpModule" preCondition="managedHandler" />
    </modules>

Then I can get code to run before DirectoryListingModule hijacks the process, but I haven't found out what to do about it when I've detected it's about to hit a physical folder.

using System.IO;
using System.Web;
using System.Linq;
using System.Linq.Expressions;

namespace MyCompany.Types
{
public class CustomHttpModule : IHttpModule
{
    public void OnAcquireRequestState(object sender, EventArgs ea)
    {
        List<string> physicalFolders = Directory.EnumerateDirectories(AppContext.BaseDirectory).Select(f => f.Substring(1 + f.LastIndexOf('\\'))).ToList();
        string projectBase = /* get from web.config */.TrimStart('/');

        string possiblePhysicalFolder = application.Request.Url.AbsolutePath.TrimStart('/').Replace(projectBase, "").TrimStart('/');
        if (physicalFolders.Exists(f => possiblePhysicalFolder.StartsWith(f)))
            /* what to do??? */;
    }
查看更多
Luminary・发光体
4楼-- · 2020-02-12 09:33

I think if you want to separate your controllers in folders, so they are not under the "controllers", you should use "areas" feature that MVC provides. It is designed for this purpose.

查看更多
登录 后发表回答