【问题标题】:How to modify ASP.NET MVC static file root如何修改 ASP.NET MVC 静态文件根目录
【发布时间】:2011-09-14 22:26:38
【问题描述】:

我希望能够重新组织我的 ASP.NET MVC 站点结构,以更接近 Rails 的方式(我们在我的公司同时使用 rails 和 ASP.net)。

在 Rails 中,有一个“公共”文件夹作为站点的根目录。例如,我可以放入“test.html”并使用 URL http://domain.com/test.html 访问该文件,这将提供静态 html 文件。

在 asp.net MVC 中有一个“内容”文件夹,我想作为根文件夹。因此,我不想访问http://domain.com/content/myfile.html,而是希望能够访问http://domain.com/myfile.html

我知道我可以将文件放到项目的根目录中,但我需要对包括 css、js、html、图像等在内的许多文件执行此操作,并且希望跨 rails 和 aspnetmvc 共享一些标准化资产。

有没有办法做到这一点?

【问题讨论】:

    标签: c# asp.net-mvc asp.net-mvc-3 routing


    【解决方案1】:

    还有另一种可能的解决方案。您可以使用重写规则来为您处理此问题,而不是使用代码。如果您使用的是 IIS 7 或更高版本,则可以使用 Microsoft 的 URL 重写模块。

    类似以下的规则可能会做到这一点:

    <rule name="Rewrite static files to content" stopProcessing="true">
      <match url="^([^/]+(?:\.css|\.js))$" />
      <conditions>
          <add input="{APPL_PHYSICAL_PATH}content{SCRIPT_NAME}" matchType="IsFile" />
      </conditions>
      <action type="Rewrite" url="/content/{R:1}" />
    </rule>
    

    该规则检查对站点根目录下的 css 或 js 文件的请求。然后它检查文件是否存在于内容文件夹中。如果存在,则重写将返回内容文件夹中的文件。我只测试了一点,但它似乎工作。它当然需要更多的测试和可能的改进。

    【讨论】:

    • 这似乎是在工作量和性能方面最有效的方法。我会试试这个。谢谢!
    【解决方案2】:

    我能想到的唯一解决方案是使用自定义控制器和路由来为您执行此操作。但这不是一个干净的解决方案。

    首先,您需要一个带有 GetFile 操作方法的 PublicController 类。这假定所有文件都直接位于 public/content 文件夹中。处理文件夹会使事情变得更加复杂。

    public class PublicController : Controller
    {
        private IDictionary<String, String> mimeTypes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
                                                            {{"css", "text/css"}, {"jpg", "image/jpg"}};
    
        public ActionResult GetFile(string file)
        {
            var path = Path.Combine(Server.MapPath("~/Content"), file);
    
            if (!System.IO.File.Exists(path)) throw new HttpException(404, "File Not Found");
            var extension = GetExtension(file); // psuedocode
            var mimetype = mimeTypes.ContainsKey(extension) ? mimeTypes[extension] : "text/plain";
            return File(path, mimetype);
        }
    }
    

    现在,您只需要一条靠近路线列表底部的路线,如下所示:

    routes.MapRoute("PublicContent", "{file}", new {controller = "Public", action = "GetFile"});
    

    问题是,现在当您只输入一个控制器名称(如“Home”)而不是默认为 HomeController 上的 Index 操作方法时,它假定您要从内容目录下载一个名为“Home”的文件。因此,在文件路由之上,您需要为每个控制器添加一个路由,以便它知道获取 Index 操作。

    routes.MapRoute("HomeIndex", "Home", new { controller = "Home", action = "Index" });
    

    因此,解决此问题的一种方法是将路线更改为:

    routes.MapRoute("PublicContent", "{file}.{extension}", new {controller = "Public", action = "GetFile"});
    

    以及对此的操作方法:

        public ActionResult GetFile(string file, string extension)
        {
            var path = Path.Combine(Server.MapPath("~/Content"), file + "." + extension);
    
            if (!System.IO.File.Exists(path)) throw new HttpException(404, "File Not Found");
    
            var mimetype = mimeTypes.ContainsKey(extension) ? mimeTypes[extension] : "text/plain";
            return File(path, mimetype);
        }
    

    就像我说的,这假设所有文件都在内容目录中,而不是在子文件夹中。但是,如果你想做 Content/css/site.css 之类的子文件夹,你可以像这样添加你的路由:

            routes.MapRoute("PublicContent_sub", "{subfolder}/{file}.{extension}", new { controller = "Public", action = "GetFileInFolder" });
            routes.MapRoute("PublicContent", "{file}.{extension}", new { controller = "Public", action = "GetFile"});
    

    现在动作方法也必须改变。

        public ActionResult GetFile(string file, string extension)
        {
            return GetFileInFolder("", file, extension);
        }
    
        public ActionResult GetFileInFolder(string subfolder, string file, string extension)
        {
            var path = Path.Combine(Server.MapPath("~/Content"), subfolder, file + "." + extension);
    
            if (!System.IO.File.Exists(path)) throw new HttpException(404, "File Not Found");
    
            var mimetype = mimeTypes.ContainsKey(extension) ? mimeTypes[extension] : "text/plain";
            return File(path, mimetype);
        }
    

    如果您开始在文件夹结构中获得多个层次,这会变得越来越丑陋。但也许这对你有用。我确定您希望项目属性中有一个复选框,但如果有的话,我不知道。

    【讨论】:

    • 感谢您花时间解释这一切。是的,这比我希望的要复杂一些。我希望有一种方法可以覆盖类似于您可以创建自己的 viewEngine 来覆盖 ViewPathLocations 的方式。
    • @AlbertVo - 是的,据我所知没有类似的。约定优于配置有时意味着事情不会按照你想要的方式工作。但肯定有可能我也不知道。
    • 这肯定行得通,但我不喜欢这个想法,因为它强制每个对静态文件的请求都经过整个框架管道。 IIS7 将直接更快地提供静态文件很多。 URL 重写规则应该提供更好的性能。
    • @Bryan Migliorisi - 我完全同意。将这种开销添加到静态文件请求中并不是一个好主意。我没有想到重写规则,但这很有意义。当然,如果您可以不理会 MVC 的约定,那就更好了。但看到路由功能如此强大和灵活,真是太酷了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多