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



我有一个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" />

这将是与该地区的国家和行动相同的路线。

如何实现此路由?

研究了Behnam Esmaili提到的问题,读得越来越深入:-)这就是我想到的。

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

注意:在线的Microsoft文档"演练:创建和注册自定义HTTP模块"建议您将HTTPModule类放在App_Code文件夹中。不要!该文件夹中的类由ASP在运行时编译。Net,导致临时中类的一个binry副本。Net文件夹,这反过来又会在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() { }

最新更新