ASP.NET Core MVC 修改视图的默认路径及其实现原理解

本章将和大家分享如何在ASP.NET Core MVC中修改视图的默认路径,以及它的实现原理。

导语:在日常工作过程中你可能会遇到这样的一种需求,就是在访问同一个页面时PC端和移动端显示的内容和风格是不一样(类似两个不一样的主题),但是它们的后端代码又是差不多的,此时我们就希望能够使用同一套后端代码,然后由系统自动去判断到底是PC端访问还是移动端访问,如果是移动端访问就优先匹配移动端的视图,在没有匹配到的情况下才去匹配PC端的视图。

下面我们就来看下这个功能要如何实现,Demo的目录结构如下所示:

ASP.NET Core MVC 修改视图的默认路径及其实现原理解

本Demo的Web项目为ASP.NET Core Web 应用程序(目标框架为.NET Core 3.1) MVC项目。

首先需要去扩展视图的默认路径,如下所示:

using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Razor; namespace NETCoreViewLocationExpander.ViewLocationExtend { /// <summary> /// 视图默认路径扩展 /// </summary> public class TemplateViewLocationExpander : IViewLocationExpander { /// <summary> /// 扩展视图默认路径(PS:并非每次请求都会执行该方法) /// </summary> /// <param></param> /// <param></param> /// <returns></returns> public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations) { var template = context.Values["template"] ?? TemplateEnum.Default.ToString(); if (template == TemplateEnum.WeChatArea.ToString()) { string[] weChatAreaViewLocationFormats = { "/Areas/{2}/WeChatViews/{1}/{0}.cshtml", "/Areas/{2}/WeChatViews/Shared/{0}.cshtml", "/WeChatViews/Shared/{0}.cshtml" }; //weChatAreaViewLocationFormats值在前--优先查找weChatAreaViewLocationFormats(即优先查找移动端目录) return weChatAreaViewLocationFormats.Union(viewLocations); } else if (template == TemplateEnum.WeChat.ToString()) { string[] weChatViewLocationFormats = { "/WeChatViews/{1}/{0}.cshtml", "/WeChatViews/Shared/{0}.cshtml" }; //weChatViewLocationFormats值在前--优先查找weChatViewLocationFormats(即优先查找移动端目录) return weChatViewLocationFormats.Union(viewLocations); } return viewLocations; } /// <summary> /// 往ViewLocationExpanderContext.Values里面添加键值对(PS:每次请求都会执行该方法) /// </summary> /// <param></param> public void PopulateValues(ViewLocationExpanderContext context) { var userAgent = context.ActionContext.HttpContext.Request.Headers["User-Agent"].ToString(); var isMobile = IsMobile(userAgent); var template = TemplateEnum.Default.ToString(); if (isMobile) { var areaName = //区域名称 context.ActionContext.RouteData.Values.ContainsKey("area") ? context.ActionContext.RouteData.Values["area"].ToString() : ""; var controllerName = //控制器名称 context.ActionContext.RouteData.Values.ContainsKey("controller") ? context.ActionContext.RouteData.Values["controller"].ToString() : ""; if (!string.IsNullOrEmpty(areaName) && !string.IsNullOrEmpty(controllerName)) //访问的是区域 { template = TemplateEnum.WeChatArea.ToString(); } else { template = TemplateEnum.WeChat.ToString(); } } context.Values["template"] = template; //context.Values会参与ViewLookupCache缓存Key(cacheKey)的生成 } /// <summary> /// 判断是否是移动端 /// </summary> /// <param></param> /// <returns></returns> protected bool IsMobile(string userAgent) { userAgent = userAgent.ToLower(); if (userAgent == "" || userAgent.IndexOf("mobile") > -1 || userAgent.IndexOf("mobi") > -1 || userAgent.IndexOf("nokia") > -1 || userAgent.IndexOf("samsung") > -1 || userAgent.IndexOf("sonyericsson") > -1 || userAgent.IndexOf("mot") > -1 || userAgent.IndexOf("blackberry") > -1 || userAgent.IndexOf("lg") > -1 || userAgent.IndexOf("htc") > -1 || userAgent.IndexOf("j2me") > -1 || userAgent.IndexOf("ucweb") > -1 || userAgent.IndexOf("opera mini") > -1 || userAgent.IndexOf("android") > -1 || userAgent.IndexOf("transcoder") > -1) { return true; } return false; } } /// <summary> /// 模板枚举 /// </summary> public enum TemplateEnum { Default = 1, WeChat = 2, WeChatArea = 3 } }

接着修改Startup.cs类,如下所示:

using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using NETCoreViewLocationExpander.ViewLocationExtend; namespace NETCoreViewLocationExpander { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); services.Configure<RazorViewEngineOptions>(options => { options.ViewLocationExpanders.Add(new TemplateViewLocationExpander()); //视图默认路径扩展 }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "areas", pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"); endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); } } }

此外,Demo中还准备了两套视图:

其中PC端视图如下所示:

ASP.NET Core MVC 修改视图的默认路径及其实现原理解

其中移动端视图如下所示:

ASP.NET Core MVC 修改视图的默认路径及其实现原理解

最后,我们分别使用PC端和移动端 来访问相关页面,如下所示:

1、访问 /App/Home/Index 页面

使用PC端访问,运行结果如下:

ASP.NET Core MVC 修改视图的默认路径及其实现原理解

使用移动端访问,运行结果如下:

ASP.NET Core MVC 修改视图的默认路径及其实现原理解

此时没有对应的移动端视图,所以都返回PC端的视图内容。

2、访问 /App/Home/WeChat 页面

使用PC端访问,运行结果如下:

ASP.NET Core MVC 修改视图的默认路径及其实现原理解

使用移动端访问,运行结果如下:

ASP.NET Core MVC 修改视图的默认路径及其实现原理解

此时有对应的移动端视图,所以当使用移动端访问时返回的是移动端的视图内容,而使用PC端访问时返回的则是PC端的视图内容。

下面我们结合ASP.NET Core源码来分析下其实现原理:

ASP.NET Core源码下载地址:https://github.com/dotnet/aspnetcore

ASP.NET Core MVC 修改视图的默认路径及其实现原理解

点击Source code下载,下载完成后,点击Release:

ASP.NET Core MVC 修改视图的默认路径及其实现原理解

ASP.NET Core MVC 修改视图的默认路径及其实现原理解

可以将这个extensions源码一起下载下来,下载完成后如下所示:

解压后我们重点来关注Razor视图引擎(RazorViewEngine.cs):

ASP.NET Core MVC 修改视图的默认路径及其实现原理解

RazorViewEngine.cs 源码如下所示:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwwpzg.html