如何修改ASP.NET MVC静态文件root

时间:2011-06-14 18:08:25

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

我希望能够重新组织我的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,images等在内的许多文件执行此操作,并希望在rails和aspnetmvc之间共享一些标准化资产。< / p>

有办法做到这一点吗?

2 个答案:

答案 0 :(得分:8)

还有另一种可能的解决方案。您可以使用重写规则来代替使用代码。如果您使用的是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文件的请求。然后它检查文件是否存在于内容文件夹中。如果存在,则重写将返回内容文件夹中的文件。我只测试了一下,但似乎有效。它当然需要更多的测试和可能的改进。

答案 1 :(得分: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”的文件。因此,在文件路由之上,您需要为每个控制器添加路由,以便它知道获取索引操作。

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);
    }

如果你开始在文件夹结构中获得多个级别,这会变得更加丑陋和丑陋。但也许这对你有用。我确定你希望在项目属性中有一个复选框,但是如果有的话,我不知道它。