ASP.NET MVC:多模板支持
背景
准备写个博客练习一下WEB编程,有一个需求就是多模板支持,类似博客园的自定义模板一样,在ASP.NET MVC中如何处理这个需求呢?
需求
描述
允许自定义模板,比如:传统模板、Metro模板等
模板结构
实现思路1
重写模板引擎的默认搜索路径
1 private void SetTemplate(string template) 2 { 3 var razorViewEngine = ViewEngines.Engines.First(x => x is RazorViewEngine) as RazorViewEngine; 4 5 razorViewEngine.ViewLocationFormats = razorViewEngine.ViewLocationFormats.Select(x => 6 { 7 return x.Replace("~/Views", string.Format("~/Views/Front/Templates/{0}", template)); 8 }).ToArray(); 9 razorViewEngine.MasterLocationFormats = razorViewEngine.ViewLocationFormats.Select(x => 10 { 11 return x.Replace("~/Views", string.Format("~/Views/Front/Templates/{0}", template)); 12 }).ToArray(); 13 razorViewEngine.PartialViewLocationFormats = razorViewEngine.ViewLocationFormats.Select(x => 14 { 15 return x.Replace("~/Views", string.Format("~/Views/Front/Templates/{0}", template)); 16 }).ToArray(); 17 }
分析
这回导致全局的修改,或者应该增加一个搜索路径(这里就不测试了),因为有些视图是不用多模板支持的,因此这种方式不太适合。
实现思路2
为VIewResult指定路径
1 public ActionResult Index(string template = "Classic") 2 { 3 return this.View("~/Views/Front/Templates/" + template + "/Home/Index.cshtml"); 4 }
分析
这种非常灵活,符合需要,但是代码看起来不够漂亮,好在MVC非常灵活,可以用Filter机制帮我们处理。
实现思路3
Filter机制
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Web.Mvc; 7 using System.Threading; 8 9 using Happy.Bootstrap; 10 11 namespace Happy.Web.Mvc.Template 12 { 13 /// <summary> 14 /// 模板相关。 15 /// </summary> 16 public sealed class TemplateRelevantAttribute : ActionFilterAttribute 17 { 18 /// <inheritdoc /> 19 public override void OnResultExecuting(ResultExecutingContext filterContext) 20 { 21 var viewResult = filterContext.Result as ViewResult; 22 if (viewResult != null) 23 { 24 var currentUserTemplate = this.GetCurrentUserTemplate(); 25 var template = string.IsNullOrEmpty(currentUserTemplate) ? TemplateService.DefaultTemplateName : currentUserTemplate; 26 var controller = filterContext.RequestContext.RouteData.Values["Controller"].ToString(); 27 var action = filterContext.RequestContext.RouteData.Values["Action"].ToString(); 28 29 if (string.IsNullOrWhiteSpace(viewResult.ViewName)) 30 { 31 viewResult.ViewName = string.Format( 32 "~/Views/{0}/{1}/{2}/{3}.{4}", 33 TemplateService.TemplateDirectoryName, 34 template, 35 controller, 36 action, 37 TemplateService.TemplateFileExtension); 38 39 return; 40 } 41 } 42 43 base.OnResultExecuting(filterContext); 44 } 45 46 private string GetCurrentUserTemplate() 47 { 48 return TemplateService.Current.GetTemplate(Thread.CurrentPrincipal.Identity.Name); 49 } 50 } 51 }
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 using Happy.Infrastructure.ExtentionMethods; 8 9 namespace Happy.Web.Mvc.Template 10 { 11 /// <summary> 12 /// 获取或访问<see cref="ITemplateService"/>实例的唯一入口。 13 /// </summary> 14 public static class TemplateService 15 { 16 private static readonly EmptyTemplateService _DefaultCommandService = new EmptyTemplateService(); 17 18 private static TemplateServiceProvider currentProvider = () => _DefaultCommandService; 19 20 static TemplateService() 21 { 22 TemplateDirectoryName = "Templates"; 23 DefaultTemplateName = "Default"; 24 TemplateFileExtension = "cshtml"; 25 } 26 27 /// <summary> 28 /// 获取当前应用程序正在使用的模板服务。 29 /// </summary> 30 public static ITemplateService Current 31 { 32 get { return currentProvider(); } 33 } 34 35 /// <summary> 36 /// 设置当前应用程序正在使用的模板服务提供者。 37 /// </summary> 38 public static void SetProvider(TemplateServiceProvider provider) 39 { 40 provider.MustNotNull("provider"); 41 42 currentProvider = provider; 43 } 44 45 /// <summary> 46 /// 模板路径。 47 /// </summary> 48 public static string TemplateDirectoryName { get; set; } 49 50 /// <summary> 51 /// 默认模板。 52 /// </summary> 53 public static string DefaultTemplateName { get; set; } 54 55 /// <summary> 56 /// 默认模板。 57 /// </summary> 58 public static string TemplateFileExtension { get; set; } 59 } 60 }
分析
采用FIlter这种AOP机制,让调用代码看起来非常漂亮,最终就采用这种方式。
测试
代码
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Mvc; 6 7 using Happy.Web.Mvc.Template; 8 9 namespace Happy.MvcExample.Controllers 10 { 11 [TemplateRelevant] 12 public class HomeController : Controller 13 { 14 // 15 // GET: /Home/ 16 17 public ActionResult Index() 18 { 19 return View(); 20 } 21 22 // 23 // GET: /Home/ 24 25 public ActionResult ChangeTemplate(string template) 26 { 27 TemplateService.DefaultTemplateName = template ?? TemplateService.DefaultTemplateName; 28 29 return this.RedirectToAction("Index"); 30 } 31 } 32 }
运行效果
备注
ViewResult的ViewName可以是绝对路径,也可以是相对路径,默认的相对路径是相对于Controller目录。
最后补上代码:http://yunpan.cn/QnYjcK3hTB3qk。