分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 技术分享

ASP.NET Core Razor生成Html静态文件

发布时间:2023-09-06 01:59责任编辑:郭大石关键词:.NET

一、前言

最近做项目的时候,使用Util进行开发,使用Razor写前端页面。初次使用感觉还是不大习惯,之前都是前后端分离的方式开发的,但是使用Util封装后的Angular后,感觉开发效率还是杠杠滴。

二、问题

在发布代码的时候,Webpack打包异常,提示是缺少了某些Html文件,我看了下相应的目录,发现目录缺少了部分Html文件,然后就问了何镇汐大大,给出的解决方案是,每个页面都需要访问一下才能生成相应的Html静态文件。这时候就产生了疑虑,是否有一种方式能获取所有路由,然后只需访问一次即可生成所有的Html页面。

三、解决方案

3.1 每次访问生成Html

解决方案思路:

  • 继承ActionFilterAttribute特性,重写执行方法
  • 访问的时候判断访问的Result是否ViewResult,如果是方可生成Html
  • RazorViewEngine中查找到View后进行渲染
/// <summary>/// 生成Html静态文件/// </summary>public class HtmlAttribute : ActionFilterAttribute { ???/// <summary> ???/// 生成路径,相对根路径,范例:/Typings/app/app.component.html ???/// </summary> ???public string Path { get; set; } ???/// <summary> ???/// 路径模板,范例:Typings/app/{area}/{controller}/{controller}-{action}.component.html ???/// </summary> ???public string Template { get; set; } ???/// <summary> ???/// 执行生成 ???/// </summary> ???public override async Task OnResultExecutionAsync( ResultExecutingContext context, ResultExecutionDelegate next ) { ???????await WriteViewToFileAsync( context ); ???????await base.OnResultExecutionAsync( context, next ); ???} ???/// <summary> ???/// 将视图写入html文件 ???/// </summary> ???private async Task WriteViewToFileAsync( ResultExecutingContext context ) { ???????try { ???????????var html = await RenderToStringAsync( context ); ???????????if( string.IsNullOrWhiteSpace( html ) ) ???????????????return; ???????????var path = Util.Helpers.Common.GetPhysicalPath( string.IsNullOrWhiteSpace( Path ) ? GetPath( context ) : Path ); ???????????var directory = System.IO.Path.GetDirectoryName( path ); ???????????if( string.IsNullOrWhiteSpace( directory ) ) ???????????????return; ???????????if( Directory.Exists( directory ) == false ) ???????????????Directory.CreateDirectory( directory ); ???????????File.WriteAllText( path, html ); ???????} ???????catch( Exception ex ) { ???????????ex.Log( Log.GetLog().Caption( "生成html静态文件失败" ) ); ???????} ???} ???/// <summary> ???/// 渲染视图 ???/// </summary> ???protected async Task<string> RenderToStringAsync( ResultExecutingContext context ) { ???????string viewName = ""; ???????object model = null; ???????if( context.Result is ViewResult result ) { ???????????viewName = result.ViewName; ???????????viewName = string.IsNullOrWhiteSpace( viewName ) ? context.RouteData.Values["action"].SafeString() : viewName; ???????????model = result.Model; ???????} ???????var razorViewEngine = Ioc.Create<IRazorViewEngine>(); ???????var tempDataProvider = Ioc.Create<ITempDataProvider>(); ???????var serviceProvider = Ioc.Create<IServiceProvider>(); ???????var httpContext = new DefaultHttpContext { RequestServices = serviceProvider }; ???????var actionContext = new ActionContext( httpContext, context.RouteData, new ActionDescriptor() ); ???????using( var stringWriter = new StringWriter() ) { ???????????var viewResult = razorViewEngine.FindView( actionContext, viewName, true ); ???????????if( viewResult.View == null ) ???????????????throw new ArgumentNullException( $"未找到视图: {viewName}" ); ???????????var viewDictionary = new ViewDataDictionary( new EmptyModelMetadataProvider(), new ModelStateDictionary() ) { Model = model }; ???????????var viewContext = new ViewContext( actionContext, viewResult.View, viewDictionary, new TempDataDictionary( actionContext.HttpContext, tempDataProvider ), stringWriter, new HtmlHelperOptions() ); ???????????await viewResult.View.RenderAsync( viewContext ); ???????????return stringWriter.ToString(); ???????} ???} ???/// <summary> ???/// 获取Html默认生成路径 ???/// </summary> ???protected virtual string GetPath( ResultExecutingContext context ) { ???????var area = context.RouteData.Values["area"].SafeString(); ???????var controller = context.RouteData.Values["controller"].SafeString(); ???????var action = context.RouteData.Values["action"].SafeString(); ???????var path = Template.Replace( "{area}", area ).Replace( "{controller}", controller ).Replace( "{action}", action ); ???????return path.ToLower(); ???}}

3.2 一次访问生成所有Html

解决方案思路:

  • 获取所有已注册的路由
  • 获取使用RazorHtml自定义特性的路由
  • 忽略Api接口的路由
  • 构建RouteData信息,用于在RazorViewEngine中查找到相应的视图
  • 构建ViewContext用于渲染出Html字符串
  • 将渲染得到的Html字符串写入文件

获取所有注册的路由,此处是比较重要的,其他地方也可以用到。

/// <summary>/// 获取所有路由信息/// </summary>/// <returns></returns>public IEnumerable<RouteInformation> GetAllRouteInformations(){ ???List<RouteInformation> list = new List<RouteInformation>(); ???var actionDescriptors = this._actionDescriptorCollectionProvider.ActionDescriptors.Items; ???foreach (var actionDescriptor in actionDescriptors) ???{ ???????RouteInformation info = new RouteInformation(); ???????if (actionDescriptor.RouteValues.ContainsKey("area")) ???????{ ???????????info.AreaName = actionDescriptor.RouteValues["area"]; ???????} ???????// Razor页面路径以及调用 ???????if (actionDescriptor is PageActionDescriptor pageActionDescriptor) ???????{ ???????????info.Path = pageActionDescriptor.ViewEnginePath; ???????????info.Invocation = pageActionDescriptor.RelativePath; ???????} ???????// 路由属性路径 ???????if (actionDescriptor.AttributeRouteInfo != null) ???????{ ???????????info.Path = $"/{actionDescriptor.AttributeRouteInfo.Template}"; ???????} ???????// Controller/Action 的路径以及调用 ???????if (actionDescriptor is ControllerActionDescriptor controllerActionDescriptor) ???????{ ???????????if (info.Path.IsEmpty()) ???????????{ ???????????????info.Path = ???????????????????$"/{controllerActionDescriptor.ControllerName}/{controllerActionDescriptor.ActionName}"; ???????????} ???????????var controllerHtmlAttribute = controllerActionDescriptor.ControllerTypeInfo.GetCustomAttribute<RazorHtmlAttribute>(); ???????????if (controllerHtmlAttribute != null) ???????????{ ???????????????info.FilePath = controllerHtmlAttribute.Path; ???????????????info.TemplatePath = controllerHtmlAttribute.Template; ???????????} ???????????var htmlAttribute = controllerActionDescriptor.MethodInfo.GetCustomAttribute<RazorHtmlAttribute>(); ???????????if (htmlAttribute != null) ???????????{ ???????????????info.FilePath = htmlAttribute.Path; ???????????????info.TemplatePath = htmlAttribute.Template; ???????????} ???????????info.ControllerName = controllerActionDescriptor.ControllerName; ???????????info.ActionName = controllerActionDescriptor.ActionName; ???????????info.Invocation = $"{controllerActionDescriptor.ControllerName}Controller.{controllerActionDescriptor.ActionName}"; ???????} ???????info.Invocation += $"({actionDescriptor.DisplayName})"; ???????list.Add(info); ???} ???return list;}

生成Html静态文件

/// <summary>/// 生成Html文件/// </summary>/// <returns></returns>public async Task Generate(){ ???foreach (var routeInformation in _routeAnalyzer.GetAllRouteInformations()) ???{ ???????// 跳过API的处理 ???????if (routeInformation.Path.StartsWith("/api")) ???????{ ???????????continue; ???????} ???????await WriteViewToFileAsync(routeInformation); ???}}/// <summary>/// 渲染视图为字符串/// </summary>/// <param name="info">路由信息</param>/// <returns></returns>public async Task<string> RenderToStringAsync(RouteInformation info){ ???var razorViewEngine = Ioc.Create<IRazorViewEngine>(); ???var tempDataProvider = Ioc.Create<ITempDataProvider>(); ???var serviceProvider = Ioc.Create<IServiceProvider>(); ???var routeData = new RouteData(); ???if (!info.AreaName.IsEmpty()) ???{ ???????routeData.Values.Add("area", info.AreaName); ???} ???if (!info.ControllerName.IsEmpty()) ???{ ???????routeData.Values.Add("controller", info.ControllerName); ???} ???if (!info.ActionName.IsEmpty()) ???{ ???????routeData.Values.Add("action", info.ActionName); ???} ???var httpContext = new DefaultHttpContext { RequestServices = serviceProvider }; ???var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor()); ???var viewResult = razorViewEngine.FindView(actionContext, info.ActionName, true); ???if (!viewResult.Success) ???{ ???????throw new InvalidOperationException($"找不到视图模板 {info.ActionName}"); ???} ???using (var stringWriter = new StringWriter()) ???{ ???????var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()); ???????var viewContext = new ViewContext(actionContext, viewResult.View, viewDictionary, new TempDataDictionary(actionContext.HttpContext, tempDataProvider), stringWriter, new HtmlHelperOptions()); ???????await viewResult.View.RenderAsync(viewContext); ???????return stringWriter.ToString(); ???}}/// <summary>/// 将视图写入文件/// </summary>/// <param name="info">路由信息</param>/// <returns></returns>public async Task WriteViewToFileAsync(RouteInformation info){ ???try ???{ ???????var html = await RenderToStringAsync(info); ???????if (string.IsNullOrWhiteSpace(html)) ???????????return; ???????var path = Utils.Helpers.Common.GetPhysicalPath(string.IsNullOrWhiteSpace(info.FilePath) ? GetPath(info) : info.FilePath); ???????var directory = System.IO.Path.GetDirectoryName(path); ???????if (string.IsNullOrWhiteSpace(directory)) ???????????return; ???????if (Directory.Exists(directory) == false) ???????????Directory.CreateDirectory(directory); ???????File.WriteAllText(path, html); ???} ???catch (Exception ex) ???{ ???????ex.Log(Log.GetLog().Caption("生成html静态文件失败")); ???}}protected virtual string GetPath(RouteInformation info){ ???var area = info.AreaName.SafeString(); ???var controller = info.ControllerName.SafeString(); ???var action = info.ActionName.SafeString(); ???var path = info.TemplatePath.Replace("{area}", area).Replace("{controller}", controller).Replace("{action}", action); ???return path.ToLower();}

四、使用方式

  • MVC控制器配置

  • Startup配置

  • 一次性生成方式,调用一次接口即可

五、源码地址

Util
Bing.NetCore
Razor生成静态Html文件:https://github.com/dotnetcore/Util/tree/master/src/Util.Webs/Razors 或者 https://github.com/bing-framework/Bing.NetCore/tree/master/src/Bing.Webs/Razors

六、参考

获取所有已注册的路由:https://github.com/kobake/AspNetCore.RouteAnalyzer

ASP.NET Core Razor生成Html静态文件

原文地址:https://www.cnblogs.com/jianxuanbing/p/9183359.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved