ASP.NET Core 中的SEO优化(4):自定义视图路径及主题切换
系列回顾
- 《ASP.NET Core 中的SEO优化(1):中间件实现服务端静态化缓存》
 - 《ASP.NET Core 中的SEO优化(2):中间件中渲染Razor视图》
 - 《ASP.NET Core 中的SEO优化(3):自定义路由匹配和生成》
 
背景
切换主题,是博客、CMS等系统的必备功能,一般来说,有三种切换主题的需求。
- 在管理后台上传主题包,并选择主题
 - 前端自动按照频道、栏目等切换模版
 - 用户在前端切换主题,并记录用户的选择
 
这三种需求,其实核心原理都是一样,就是制定一套主题的目录,切换主题等于切换目录名。主题内的页面模版都是按照一定的规则存放的。
下面是两个主题包的目录示例:
  .
  ├── theme0
  |   ├── Assets
  |   |   ├── js
  |   |   ├── css
  |   |   └── img
  |   ├── Home
  |   |   ├── Index.cshtml
  |   |   └── About.cshtml
  |   ├── Article
  |   |   ├── Index.cshtml
  |   |   └── Detail.cshtml
  |   └── Shared
  |       ├── Page.cshtml
  |       └──  _Layout.cshtml
  └── theme1
      ├── Assets
      |   ├── js
      |   ├── css
      |   └── img
      ├── Home
      |   ├── Index.cshtml
      |   └── About.cshtml
      ├── Article
      |   ├── Index.cshtml
      |   └── Detail.cshtml
      └── Shared
          ├── Page.cshtml
          └──  _Layout.cshtml
大家一定注意到了,上面每个主题包里都按照传统ASP.NET MVC的约定来划分目录:控制器名为文件夹,操作名为视图文件。其实这里只是方便起见,按照接下来介绍的方法,是可以完全地自定义这个目录划分的。
原理
当ASP.NET MVC从控制器处理完数据返回视图的时候,ASP.NET MVC会按照默认的多个路径去查找文件,如果文件存在,则使用该文件渲染,如果不存在,则寻找下一个路径,比如默认的路径会有/{Area}/{Controller}/{Action}.cshtml、/{Controller}/{Action}.cshtml、/Shared/{Action}.cshtml等等我们熟悉的约定,那么在查找视图文件时,会安装从左往右的路径去查询,如果都查询不出来,是会报错的。
而如果要做到切换主题文件夹名来切换主题,我们就需要在默认规则上加主题的目录占位符,使的查询时用主题文件夹名来替换占位符,例如/{theme}/{Controller}/{Action}.cshtml、/{theme}/Shared/{Action}.cshtml等等,这样,当查询视图文件时,就能匹配到对应的主题文件夹,并且找到相应的视图了。
总结起来,切换主题功能有两个重点需要我们去实现:
- 在原有规则中加入占位符
 - 每次请求都获取当前的主题名,并改变视图查询路径
 
实现
最简单的实现,在操作(action)的最后return View(viewPath)时传入视图路径,直接就能指向对应视图,但是,这样做一点都不灵活,而且每个操作都要传路径也是不够简洁,不容易维护,所以我们需要更好的解决方案。
ASP.NET MVC 实现
在ASP.NET MVC时代,我们可通过继承RazorViewEngine类,在基类的ViewLocationFormats和PartialViewLocationFormats两个属性中加入有主题目录名占位符的路径,并重写CreateView、CreatePartialView、FileExists三个方法,使每次请求都能获取最新的主题名,如下面的例子中从路由数据对象中获取主题名:
public class TemplateViewEngine : RazorViewEngine
{
    public TemplateViewEngine() : base()
    {
        ViewLocationFormats = new[] {
            "~/Views/{1}%1/{0}.cshtml",
            "~/Views/{1}/{0}.cshtml",//默认路径
            "~/Views/Shared%1/{0}.cshtml",
            "~/Views/Shared/{0}.cshtml",
        };
        PartialViewLocationFormats = new[] {
            "~/Views/{1}%1/{0}.cshtml",
            "~/Views/{1}/{0}.cshtml",//默认路径
            "~/Views/Shared%1/{0}.cshtml",
            "~/Views/Shared/{0}.cshtml",
        };
    }
    protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
    {
        var template = controllerContext.RouteData.Values["template"] != null ? "/" + controllerContext.RouteData.Values["template"].ToString() : "";
        return base.CreatePartialView(controllerContext, partialPath.Replace("%1", template));
    }
    protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
    {
        var template = controllerContext.RouteData.Values["template"] != null ? "/" + controllerContext.RouteData.Values["template"].ToString() : "";
        return base.CreateView(controllerContext, viewPath.Replace("%1", template), masterPath);
    }
    protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
    {
        var template = controllerContext.RouteData.Values["template"] != null ? "/" + controllerContext.RouteData.Values["template"].ToString() : "";
        return base.FileExists(controllerContext, virtualPath.Replace("%1", template));
    }
}
事实上,如果是需要实现不同用户不同主题的功能,主题信息可以存储在Session中,还能从controllerContext实例获取Session中存储的主题名。
那么,在ASP.NET Core中如何实现呢?
ASP.NET Core 实现
ASP.NET Core 相比ASP.NET MVC框架,虽然使用上为了开发者平滑过渡,很多约定都相同,但是架构本身是做了翻天覆地的重构和优化,得益于一脉相承的MSDI框架,ASP.NET Core框架实现了组件化,很多功能都通过IoC的方式修改或扩展。例如本文介绍的主题情况功能,就是实现IViewLocationExpander接口来达到扩展配置的目的,而且还比ASP.NET MVC的更加简洁:
public class TemplateViewLocationExpander : IViewLocationExpander
{
    public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
    {
        var template = context.Values["template"] ?? "Default";
        string[] locations = { "/Views/" + template + "/{1}/{0}.cshtml", "/Views/" + template + "/{0}.cshtml", "/Views/" + template + "/Shared/{0}.cshtml" };
        return locations.Union(viewLocations);
    }
    public void PopulateValues(ViewLocationExpanderContext context)
    {
        context.Values["template"] = context.ActionContext.RouteData.Values["Template"]?.ToString() ?? "Default";
    }
}
这个接口里面,PopulateValues方法主要用来获取实时的主题信息,context.ActionContext中除了RouteData可获得实时数据,还有HttpContext实例可获得用户信息,甚至能利用RequestServices实例注入服务。而只有在PopulateValues中修改了context,ExpandViewLocations方法才会从context中获得主题信息,从而达到修改视图查找路径的目的。
当我们实现了IViewLocationExpander接口后,还需要在Startup类的services.AddMvc();下修改MVC的配置:
services.AddMvc();
//配置模版视图路径
services.Configure<RazorViewEngineOptions>(options =>
{
    options.ViewLocationExpanders.Add(new TemplateViewLocationExpander());
});
PS:这种修改MVC内部配置的方式很有趣,以后有空会研究一番。
总结
本文主要介绍了在ASP.NET Core中利用修改视图查询路径实现主题切换的功能,虽然只介绍了核心部分,但是其它部分如管理主题、前端切换等功能,都是很容易实现的,以后我会在我的框架样例中实现,敬请大家关注啦!
ASP.NET Core 中的SEO优化(4):自定义视图路径及主题切换的更多相关文章
- ASP.NET Core 中的SEO优化(3):自定义路由匹配和生成
		
前言 前两篇文章主要总结了CMS系统两个技术点在ASP.NET Core中的应用: <ASP.NET Core 中的SEO优化(1):中间件实现服务端静态化缓存> <ASP.NET ...
 - ASP.NET Core 中的SEO优化(2):中间件中渲染Razor视图
		
前言 上一篇文章<ASP.NET Core 中的SEO优化(1):中间件实现服务端静态化缓存>中介绍了中间件的使用方法.以及使用中间件实现服务端静态化缓存的功能.本系列文章的这些技巧都是我 ...
 - ASP.NET Core 中的SEO优化(1):中间件实现服务端静态化缓存
		
分享 最近在公司成功落地了一个用ASP.NET Core 开发前台的CMS项目,虽然对于表层的开发是兼容MVC5的,但是作为爱好者当然要用尽量多的ASP.NET Core新功能了. 背景 在项目开发的 ...
 - Asp.Net Core 中IdentityServer4 授权中心之自定义授权模式
		
一.前言 上一篇我分享了一篇关于 Asp.Net Core 中IdentityServer4 授权中心之应用实战 的文章,其中有不少博友给我提了问题,其中有一个博友问我的一个场景,我给他解答的还不够完 ...
 - Asp.Net Core 中IdentityServer4 授权原理及刷新Token的应用
		
一.前言 上面分享了IdentityServer4 两篇系列文章,核心主题主要是密码授权模式及自定义授权模式,但是仅仅是分享了这两种模式的使用,这篇文章进一步来分享IdentityServer4的授权 ...
 - Asp.Net Core 中IdentityServer4 实战之 Claim详解
		
一.前言 由于疫情原因,让我开始了以博客的方式来学习和分享技术(持续分享的过程也是自己学习成长的过程),同时也让更多的初学者学习到相关知识,如果我的文章中有分析不到位的地方,还请大家多多指教:以后我会 ...
 - Asp.Net Core 中IdentityServer4 实战之角色授权详解
		
一.前言 前几篇文章分享了IdentityServer4密码模式的基本授权及自定义授权等方式,最近由于改造一个网关服务,用到了IdentityServer4的授权,改造过程中发现比较适合基于Role角 ...
 - 在ASP.NET Core中构建路由的5种方法
		
原文链接 :https://stormpath.com/blog/routing-in-asp-net-core 在ASP.NET Core中构建路由的5种方法 原文链接 :https://storm ...
 - C#调用接口注意要点  socket,模拟服务器、客户端通信  在ASP.NET Core中构建路由的5种方法
		
C#调用接口注意要点 在用C#调用接口的时候,遇到需要通过调用登录接口才能调用其他的接口,因为在其他的接口需要在登录的状态下保存Cookie值才能有权限调用, 所以首先需要通过调用登录接口来保存c ...
 
随机推荐
- 实现基本的Ajax和Json请求
			
前面已经封装好了一个方法ajax(),通过这个方法可以实现Ajax请求,接下来就是给出 例程来测试这个方法和实现简单的功能. 视图的部分代码如下: <body> <div> ...
 - 你曾后悔进入 IT 行业吗?为什么?(转自知乎)--一生不悔入IT
			
你曾后悔进入 IT 行业吗?为什么?(转自知乎)--一生不悔入IT 一.总结 一句话总结:看了大概200条评论,99%的不后悔,大部分人后悔没有早点干,但是做it最最主要的是要注意身体. 1.it是最 ...
 - Knockout结合Bootstrap创建动态UI--产品列表管理
			
本篇文章结合Bootstrap创建一个比较完整的应用,对产品列表进行管理,包括产品的增加.删除.修改. 需要的引用 <script type='text/javascript' src='htt ...
 - nyoj38——最小生成树
			
布线问题 时间限制:1000 ms | 内存限制:65535 KB 难度:4 描述 南阳理工学院要进行用电线路改造,现在校长要求设计师设计出一种布线方式,该布线方式需要满足以下条件:1.把所有 ...
 - proxy-target-class 作用
			
该属性值默认为false,表示使用JDK动态代理织入增强;当值为true时,表示使用CGLib动态代理织入增强;但是,即使设置为false,如果目标类没有生命接口, 则Spring将自动使用CGLib ...
 - iOS Layout机制相关方法
			
iOS Layout机制相关方法 - (CGSize)sizeThatFits:(CGSize)size - (void)sizeToFit ——————- - (void)layoutSubview ...
 - Ubuntu下压缩解压文件
			
一般来说ubuntu 下带有tar 命令,可以用来解压和压缩之用.但是我们经常要与win下用户打交道,所以要安装一些解压工具如:rar zip 等命令. 如果要需要用到zip工具那么可以: sudo ...
 - js中字符串与数组的相互转换
			
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
 - C++复习13.虚析构函数知识
			
C++ 虚析构函数 20131010 在C++中的虚函数作用是实现基于继承机制的多态,但是我们好像忽略了一种情况,就是虚析构函数.在C++继承机制中,虽然构造函数是不可以使用虚函数声明,但是析构函数是 ...
 - cf 290F. Treeland Tour 最长上升子序列 + 树的回溯 难度:1
			
F. Treeland Tour time limit per test 5 seconds memory limit per test 256 megabytes input standard in ...