话不多说,直接贴代码
一下代码根据自己需求进行修改,总的来说就是重写视图路径
net mvc:/// <summary> /// 主题路径提供者视图引擎 /// </summary> public abstract class ThemeVirtualPathProviderViewEngine : VirtualPathProviderViewEngine { //工作上下文 private static IThemeContext themeContext; //模板视图位置格式 private readonly string[] _templateslocationformats = new string[] { "~/{0}/{1}/Views/{2}.cshtml", "~/{0}/{1}/Views/Shared/{2}.cshtml", "~/Views/Shared/{2}.cshtml" }; //模板视图位置格式 private readonly string[] _arealocationformats = new string[] { //themes "~/Areas/{2}/Themes/{1}/Views/{0}.cshtml", "~/Areas/{2}/Themes/{1}/Views/Shared/{0}.cshtml", //default "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", }; protected ThemeVirtualPathProviderViewEngine() { //将视图文件扩展名限制为cshtml FileExtensions = new[] { "cshtml" }; } #region 重写方法 /// <summary> /// 使用指定的控制器上下文和母版视图名称来查找指定的视图 /// </summary> /// <param name="controllerContext">控制器上下文</param> /// <param name="viewName">视图的名称</param> /// <param name="masterName">母版视图的名称</param> /// <param name="useCache">若为 true,则使用缓存的视图</param> /// <returns>页视图</returns> public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) { //构建一个视图引擎结果 ViewEngineResult result = FindThemeView(controllerContext, viewName, masterName, useCache); return result; } /// <summary> /// 寻找分部视图的方法 /// </summary> /// <param name="controllerContext">控制器上下文</param> /// <param name="partialViewName">分部视图的名称</param> /// <param name="useCache">若为 true,则使用缓存的分部视图</param> /// <returns>分部视图</returns> public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) { //构建一个分部视图引擎结果 ViewEngineResult result = FindThemePartialView(controllerContext, partialViewName, useCache); return result; } #endregion protected virtual string GetCurrentTheme(bool mobile) { themeContext = ThemeContext.GetInstance(); if (mobile) return themeContext.WorkingMobileTheme; else return themeContext.WorkingDesktopTheme; } #region 工具方法 /// <summary> /// 获取文件的路径 /// </summary> private string GetPath(ControllerContext controllerContext, string name, string controllerName, string theme, string cacheKeyPrefix, bool useCache, out string[] searchedLocations) { searchedLocations = null; //视图位置列表 string[] locations = null; //获取区域 string area = GetRouteDataTokenValue("area", controllerContext.RouteData.DataTokens).ToLower(); if (area.Length == 0)//前台视图位置的处理 { locations = _templateslocationformats; } else { locations = _arealocationformats; } //是否为特殊路径的标识 bool flag2 = IsSpecificName(name); //从缓存中获取视图位置 string cacheKey = CreateCacheKey(cacheKeyPrefix, name, flag2 ? string.Empty : controllerName, area, theme);//视图位置的缓存键 if (useCache) { //从缓存中得到视图位置 var cachedPath = ViewLocationCache.GetViewLocation(controllerContext.HttpContext, cacheKey); if (cachedPath != null) { return cachedPath; } } //如果视图位置不在缓存中,则构建视图位置并存储到缓存中 if (!flag2)//不是特殊路径时的操作 { return GetPathFromGeneralName(controllerContext, locations, name, theme, controllerName, area, cacheKey, ref searchedLocations); } else//特殊路径时的操作 { return GetPathFromSpecificName(controllerContext, name, cacheKey, ref searchedLocations); } } /// <summary> /// 特殊名称时构建视图路径 /// </summary> private string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey, ref string[] searchedLocations) { //将路径添加到视图位置缓存 ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, name); //扩展名不为“.cshtml”或者文件不存在时 if (!IsSupportedExtension(name) || !FileExists(controllerContext, name)) { searchedLocations = new string[] { name }; return string.Empty; } return name; } /// <summary> /// 普通名称时构建视图路径 /// </summary> private string GetPathFromGeneralName(ControllerContext controllerContext, string[] viewLocationFormats, string name, string theme, string controllerName, string areaName, string cacheKey, ref string[] searchedLocations) { int count = viewLocationFormats.Length; searchedLocations = new string[count]; //循环视图位置 for (int i = 0; i < count; i++) { string path = string.Empty; if (!string.IsNullOrEmpty(areaName))//后台访问 { path = string.Format(viewLocationFormats[i], name, controllerName, areaName); } else { path = string.Format(viewLocationFormats[i], DTKeys.DIRECTORY_REWRITE_MVC, theme, name); } if (FileExists(controllerContext, path)) { searchedLocations = null; ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, path); return path; } //将路径添加到搜索位置列表中 searchedLocations[i] = path; } return string.Empty; } /// <summary> /// 创建视图位置的缓存键 /// </summary> private string CreateCacheKey(string prefix, string name, string controllerName, string area, string theme) { return string.Format(":ViewCacheKey:{0}:{1}:{2}:{3}:{4}:{5}", new object[] { base.GetType().AssemblyQualifiedName, prefix, name, controllerName, area, theme }); } /// <summary> /// 构建视图引擎结果 /// </summary> private ViewEngineResult FindThemeView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) { //视图文件路径搜索列表 string[] strArray1 = null; //布局文件路径搜索列表 string[] strArray2 = null; //获取控制器名称 string controllerName = controllerContext.RouteData.GetRequiredString("controller"); //获取视图文件路径 string viewPath = GetPath(controllerContext, viewName, controllerName, GetCurrentTheme(false), "View", useCache, out strArray1); //当视图文件存在时 if (!string.IsNullOrWhiteSpace(viewPath)) { if (string.IsNullOrWhiteSpace(masterName)) { return new ViewEngineResult(CreateView(controllerContext, viewPath, string.Empty), this); } else { //获取布局文件的路径 string masterPath = GetPath(controllerContext, masterName, controllerName, GetCurrentTheme(false), "Master", useCache, out strArray2); if (!string.IsNullOrWhiteSpace(masterPath)) { return new ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this); } } } //当视图文件或布局文件不存在时将搜索路径返回 if (strArray2 == null) { return new ViewEngineResult(strArray1); } else { return new ViewEngineResult(strArray1.Union<string>(strArray2)); } } /// <summary> /// 构建分部视图引擎结果 /// </summary> private ViewEngineResult FindThemePartialView(ControllerContext controllerContext, string partialViewName, bool useCache) { //分部视图文件路径搜索列表 string[] strArray; //获取控制器名称 string controllerName = controllerContext.RouteData.GetRequiredString("controller"); //获取分部视图文件路径 string partialViewPath = GetPath(controllerContext, partialViewName, controllerName, GetCurrentTheme(false), "Partial", useCache, out strArray); //当分部视图文件存在时 if (!string.IsNullOrWhiteSpace(partialViewPath)) { return new ViewEngineResult(CreatePartialView(controllerContext, partialViewPath), this); } //分部视图文件不存在时返回搜索路径 return new ViewEngineResult(strArray); } /// <summary> /// 判读视图名称是否以“~”或“/”开头 /// </summary> private bool IsSpecificName(string name) { char ch = name[0]; if (ch != '~') { return (ch == '/'); } return true; } /// <summary> /// 判读文件扩展名是否为“.cshtml” /// </summary> private bool IsSupportedExtension(string path) { return path.EndsWith(".cshtml", StringComparison.OrdinalIgnoreCase); } /// <summary> /// 获取路由集合中指定键的值 /// </summary> private string GetRouteDataTokenValue(string key, RouteValueDictionary dict) { object value = dict[key]; if (value == null) return string.Empty; return value.ToString(); } #endregion }
net core:
/// <summary> /// 指定Microsoft使用的视图位置扩展器的约定。Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine实例来确定视图的搜索路径 /// </summary> public class ThemeableViewLocationExpander : IViewLocationExpander { private const string THEME_KEY = "dtcms.themename"; /// <summary> /// 工作上下文 /// </summary> private static IThemeContext themeContext; protected virtual string GetCurrentTheme(bool mobile) { themeContext = ThemeContext.GetInstance(); if (mobile) { return themeContext.WorkingMobileTheme; } else { return themeContext.WorkingDesktopTheme; } } /// <summary> /// Invoked by a Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine to determine the /// 此实例将使用的值 Microsoft.AspNetCore.Mvc.Razor.IViewLocationExpander /// 计算出的值用于确定视图位置自上次定位以来是否发生了更改 /// </summary> /// <param name="context"></param> public void PopulateValues(ViewLocationExpanderContext context) { //根本不需要添加可主题化的视图位置,因为管理层无论如何都不应该是可主题化的 if (context.AreaName != null) { if (context.AreaName.ToLower()?.Equals("admin") ?? false) { return; } } context.Values[THEME_KEY] = GetCurrentTheme(false); } /// <summary> /// 用于确定视图的潜在位置 /// </summary> /// <param name="context">Context</param> /// <param name="viewLocations">视图位置</param> /// <returns></returns> public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations) { if (context.Values.TryGetValue(THEME_KEY, out string theme)) { viewLocations = new[] { $"/templates/{theme}/Views/0.cshtml", $"/templates/{theme}/Views/Shared/0.cshtml", } .Concat(viewLocations); } return viewLocations; } }
net core需要注意的是,还要要注册服务:
//添加主题模板 services.Configure<RazorViewEngineOptions>(options => { options.ViewLocationExpanders.Add(new ThemeableViewLocationExpander()); });