zoukankan      html  css  js  c++  java
  • ASP.NET Core 自定义视图路径及主题切换

     原文地址:https://www.cnblogs.com/ElderJames/p/Customized-View-Path-And-Theme-Switching-In-AspNetCore.html

    0|1背景


    切换主题,是博客、CMS等系统的必备功能,一般来说,有三种切换主题的需求。

    1. 在管理后台上传主题包,并选择主题
    2. 前端自动按照频道、栏目等切换模版
    3. 用户在前端切换主题,并记录用户的选择

    这三种需求,其实核心原理都是一样,就是制定一套主题的目录,切换主题等于切换目录名。主题内的页面模版都是按照一定的规则存放的。

    下面是两个主题包的目录示例:

      .
      ├── 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的约定来划分目录:控制器名为文件夹,操作名为视图文件。其实这里只是方便起见,按照接下来介绍的方法,是可以完全地自定义这个目录划分的。

    0|1原理

     

    当ASP.NET MVC从控制器处理完数据返回视图的时候,ASP.NET MVC会按照默认的多个路径去查找文件,如果文件存在,则使用该文件渲染,如果不存在,则寻找下一个路径,比如默认的路径会有/{Area}/{Controller}/{Action}.cshtml/{Controller}/{Action}.cshtml/Shared/{Action}.cshtml等等我们熟悉的约定,那么在查找视图文件时,会安装从左往右的路径去查询,如果都查询不出来,是会报错的。

    而如果要做到切换主题文件夹名来切换主题,我们就需要在默认规则上加主题的目录占位符,使的查询时用主题文件夹名来替换占位符,例如/{theme}/{Controller}/{Action}.cshtml/{theme}/Shared/{Action}.cshtml等等,这样,当查询视图文件时,就能匹配到对应的主题文件夹,并且找到相应的视图了。

    总结起来,切换主题功能有两个重点需要我们去实现:

    1. 在原有规则中加入占位符
    2. 每次请求都获取当前的主题名,并改变视图查询路径

    0|1实现


    最简单的实现,在操作(action)的最后return View(viewPath)时传入视图路径,直接就能指向对应视图,但是,这样做一点都不灵活,而且每个操作都要传路径也是不够简洁,不容易维护,所以我们需要更好的解决方案。

    ASP.NET MVC 实现

    在ASP.NET MVC时代,我们可通过继承RazorViewEngine类,在基类的ViewLocationFormatsPartialViewLocationFormats两个属性中加入有主题目录名占位符的路径,并重写CreateViewCreatePartialViewFileExists三个方法,使每次请求都能获取最新的主题名,如下面的例子中从路由数据对象中获取主题名:

    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中修改了contextExpandViewLocations方法才会从context中获得主题信息,从而达到修改视图查找路径的目的。

    当我们实现了IViewLocationExpander接口后,还需要在Startup类的services.AddMvc();下修改MVC的配置:

    services.AddMvc();
    //配置模版视图路径
    services.Configure<RazorViewEngineOptions>(options =>
    {
        options.ViewLocationExpanders.Add(new TemplateViewLocationExpander());
    });

    PS:这种修改MVC内部配置的方式很有趣,以后有空会研究一番。

    0|1总结

     

    本文主要介绍了在ASP.NET Core中利用修改视图查询路径实现主题切换的功能,虽然只介绍了核心部分,但是其它部分如管理主题、前端切换等功能,都是很容易实现的,以后我会在我的框架样例中实现,敬请大家关注啦

    在开发MVC的过程中可能遇到这种情况:我希望我的视图可以放在自定义的文件夹下,而不是放在默认的Views文件夹下,这时我就需要更改MVC的默认路径

      如图,我的移动端和PC端是两套单独设计的页面,所以我把视图拆分到了两个文件夹下:PC和Mobile,但是MVC的默认路径是找不到这两个文件夹的,所以我进行了以下更改

     

    ASP.NET Core中有一个接口IViewLocationExpander 通过继承这个接口我们可以更改,MVC访问的默认文件路径

     

    接口中的两个方法PopulateValues  方法可以让我在ViewLocationExpanderContext上下文中添加后续可能会用到的键值对

    ExpandViewLocations 方法会在MVC无法找到默认的视图路径时调用,动态的返回需要的路径

    通过这种方法,我们就可以实现更改MVC的视图默认访问路径了
    ————————————————
    版权声明:本文为CSDN博主「_Tassdar」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/CuiLanren/article/details/81566504

  • 相关阅读:
    两角和的正切
    积化和差与和差化积
    require.js的简单使用
    HTML、css、javascript、DOM编程
    SignalR长连接的简单用法
    【ESP8266】发送HTTP请求
    记录自己的第一篇博客
    1 为什么搭建.Net core下的云开发框架
    C#线程中LOCK的意义
    ping命令执行过程详解
  • 原文地址:https://www.cnblogs.com/fei686868/p/12298891.html
Copyright © 2011-2022 走看看