以与控制器和操作相同的方式路由MVC区域内容?

时间:2012-12-05 11:17:27

标签: asp.net-mvc-3 asp.net-mvc-routing asp.net-mvc-areas

我有一个带有区域的MVC 3项目,我们称之为MyArea。我想在该区域的子文件夹下放置特定于MyArea的脚本和样式,从而产生如下项目文件夹结构:

/Areas/MyArea
/Areas/MyArea/Controllers
/Areas/MyArea/Scripts   <-------- I want these here
/Areas/MyArea/Styles    <--------
/Areas/MyArea/ViewModels
/Areas/MyArea/Views
/Controllers
/Scripts
/Styles
/ViewModels
/Views

很好,但现在当我链接到文档/视图中的样式时,它必须像这样写:

<link href="/Areas/MyArea/Styles/MyStyle.css" rel="stylesheet" type="text/css" />

我更喜欢这样链接:

<link href="/MyArea/Styles/MyStyle.css" rel="stylesheet" type="text/css" />

这与该地区的控制者和行动的路线相同。

如何实现此路由?

1 个答案:

答案 0 :(得分:3)

研究了Behnam Esmaili提到的问题,并进一步阅读,进一步阅读:-)这就是我想出来的。

我创建了一个新的IHttpModule,用于检查/areaname/contentfolder/的每个传入请求的路径,其中areaname是任何应用程序区域的名称,contentfolder是任何选定的任何区域可能的内容文件夹名称列表。我选择对一组合理的内容文件夹名称进行硬编码,但您可以让每个区域注册都将其所有内容文件夹名称注册到某处并使用它。

注意:在线Microsoft文档Walkthrough: Creating and Registering a Custom HTTP Module建议您将HTTPModule类放在App_Code文件夹中。别!该文件夹中的类是由ASP.Net运行时编译的,导致temp .Net文件夹中的类的binry副本,这反过来导致ASP.Net尝试加载HTTPModule类时出现歧义。将课程放在您选择的其他文件夹中。

要查找所有区域名称,我选择使用AppDomain.CurrentDomain.GetAssemblies()并查找System.Web.Mvc.AreaRegistration的所有子类。创建每个实例并检索其AreaName属性的值。

完整源代码:

public class HTTPModuleAreaContent : IHttpModule
{
    private List<string> allAreaNames = null;

    public HTTPModuleAreaContent()
    {
    }

    public String ModuleName
    {
        get { return "HTTPModuleAreaContent"; }
    }

    public void Init(HttpApplication application)
    {
        application.BeginRequest +=
            (new EventHandler(this.BeginRequest));
    }

    private void GetAreaNames(HttpContext context)
    {
        allAreaNames = AppDomain.CurrentDomain.GetAssemblies()
            .SelectMany(ass => ass.GetTypes())
            .Where(t => t.IsSubclassOf(typeof(AreaRegistration)))
            .Select(t => ((AreaRegistration)Activator.CreateInstance(t)).AreaName)
            .ToList();
    }

    private void BeginRequest(object sender, EventArgs e)
    {
        HttpApplication application = (HttpApplication)sender;
        HttpContext context = application.Context;
        if (allAreaNames == null)
            GetAreaNames(context);

        string filePath = context.Request.FilePath.ToUpper();
        string areaName = allAreaNames
            .FirstOrDefault(an => filePath.StartsWith('/' + an + '/', StringComparison.OrdinalIgnoreCase));
        if (string.IsNullOrEmpty(areaName))
            return;
        string areaNameUpper = areaName.ToUpper();
        if (filePath.StartsWith('/' + areaNameUpper + "/STYLES/")
            || filePath.StartsWith('/' + areaNameUpper + "/SCRIPT/")
            || filePath.StartsWith('/' + areaNameUpper + "/SCRIPTS/")
            || filePath.StartsWith('/' + areaNameUpper + "/JS/")
            || filePath.StartsWith('/' + areaNameUpper + "/JAVASCRIPT/")
            || filePath.StartsWith('/' + areaNameUpper + "/JAVASCRIPTS/")
            || filePath.StartsWith('/' + areaNameUpper + "/CONTENT/")
            || filePath.StartsWith('/' + areaNameUpper + "/IMAGES/")
        )
            context.RewritePath("/Areas/" + context.Request.Path);
    }

    public void Dispose() { }
}

编辑:显然,上述解决方案不适用于不在域根目录下的应用程序。经过一些工作后,我想出了以下解决方案:

public class HTTPModuleAreaContent : IHttpModule
{
    private List<string> allAreaNames = null;
    private HashSet<string> folderNamesToRewrite = new HashSet<string>();

    public HTTPModuleAreaContent()
    {
    }

    public String ModuleName
    {
        get { return "HTTPModuleAreaContent"; }
    }

    public void Init(HttpApplication application)
    {
        application.BeginRequest +=
            (new EventHandler(this.BeginRequest));
        folderNamesToRewrite.Add("STYLES");
        folderNamesToRewrite.Add("SCRIPT");
        folderNamesToRewrite.Add("SCRIPTS");
        folderNamesToRewrite.Add("JS");
        folderNamesToRewrite.Add("JAVASCRIPT");
        folderNamesToRewrite.Add("JAVASCRIPTS");
        folderNamesToRewrite.Add("CONTENT");
        folderNamesToRewrite.Add("IMAGES");
    }

    private void GetAreaNames(HttpContext context)
    {
        allAreaNames = AppDomain.CurrentDomain.GetAssemblies().SelectMany(ass => ass.GetTypes()).Where(t => t.IsSubclassOf(typeof(AreaRegistration))).Select(t => ((AreaRegistration)Activator.CreateInstance(t)).AreaName).ToList();
    }

    private void BeginRequest(object sender, EventArgs e)
    {
        HttpApplication application = (HttpApplication)sender;
        HttpContext context = application.Context;
        if (allAreaNames == null)
            GetAreaNames(context);

        string filePath = context.Request.FilePath;
        string areaName = allAreaNames.FirstOrDefault(an => Regex.IsMatch(filePath, '/' + an + '/', RegexOptions.IgnoreCase | RegexOptions.CultureInvariant));
        if (string.IsNullOrEmpty(areaName))
            return;
        string areaNameUpper = areaName.ToUpperInvariant();
        Regex regex = new Regex('/' + areaNameUpper + "/([^/]+)/", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
        Match m = regex.Match(filePath);
        if (m.Success && m.Groups.Count > 1)
        {
            string folderName = m.Groups[1].Value;
            string folderNameUpper = folderName.ToUpperInvariant();
            if (folderNamesToRewrite.Contains(folderNameUpper))
                context.RewritePath(regex.Replace(context.Request.Path, string.Format("/Areas/{0}/{1}/", areaName, folderName), 1));
        }
    }

    public void Dispose() { }