PageModel静态化方案查找过很多资料。
基本上没找到一个使用视图引擎静态化PageModel的方案。
最终也只是解决了分部视图静态化方式,对于Layout也是无能为力。
以下实现了PageModel扩展实现了对视图渲染转换成Html。此方法不包含Layout和Section。
/// <summary>/// 转换成Html/// </summary>/// <param name="pageModel"></param>/// <param name="pageName"></param>/// <returns></returns>public async Task<string> ToHtml(this PageModel pageModel, string pageName){var actionContext = new ActionContext(pageModel.HttpContext,pageModel.RouteData,pageModel.PageContext.ActionDescriptor);using (var sw = new StringWriter()){IRazorViewEngine _razorViewEngine = pageModel.HttpContext.RequestServices.GetService(typeof(IRazorViewEngine)) as IRazorViewEngine;IRazorPageActivator _activator = pageModel.HttpContext.RequestServices.GetService(typeof(IRazorPageActivator)) as IRazorPageActivator;var result = _razorViewEngine.FindPage(actionContext, pageName);if (result.Page == null){throw new ArgumentNullException($"The page {pageName} cannot be found.");}var page = result.Page;var view = new RazorView(_razorViewEngine,_activator,new List<IRazorPage>(),page,HtmlEncoder.Default,new DiagnosticListener("ViewRenderService"));var viewContext = new ViewContext(actionContext,view,pageModel.ViewData,pageModel.TempData,sw,new HtmlHelperOptions());var pageNormal = ((Page)result.Page);pageNormal.PageContext = pageModel.PageContext;pageNormal.ViewContext = viewContext;_activator.Activate(pageNormal, viewContext);await page.ExecuteAsync();return sw.ToString();}}
但是由于某些原因,我们需要生成包含layout的整个HTML页面,所以暂时没有别的办法,只能通过ActionFilter拦截的方式获取生成后的HTML,如下方代码
/// <summary>/// Razor生成Html静态文件(保存目录为wwwroot)/// </summary>[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]public class HtmlStaticAttribute : ActionFilterAttribute{/// <summary>/// 路径模板,范例:static/{area}/{controller}/{action}.component.html/// </summary>public string Template { get; set; }/// <summary>/// 生成的最小间隔,单位(秒),比如设置5分钟,那么5分钟之内不会再生成/// </summary>public int MinInterval { get; set; } = 0;/// <summary>/// 结果执行之前 before/// </summary>/// <param name="context"></param>/// <param name="next"></param>/// <returns></returns>public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next){if ((context.Result is PageResult || context.Result is ViewResult) && IsBuildHtml(RouteReplace(context, Template))){var response = context.HttpContext.Response;if (!response.Body.CanRead || !response.Body.CanSeek){using (var ms = new MemoryStream()){var old = response.Body;response.Body = ms;await base.OnResultExecutionAsync(context, next);if (response.StatusCode == 200){await WriteHtml(context, response.Body);}ms.Position = 0;await ms.CopyToAsync(old);response.Body = old;}}else{await base.OnResultExecutionAsync(context, next);var old = response.Body.Position;if (response.StatusCode == 200){await WriteHtml(context, response.Body);}response.Body.Position = old;}}else{await base.OnResultExecutionAsync(context, next);}}/// <summary>/// 根据路由参数进行模板替换/// </summary>/// <param name="context"></param>/// <param name="template">比如:static/{area}/{controller}/{action}/{id}.html</param>/// <returns></returns>public static string RouteReplace(ActionContext context, string template){var path = template;foreach (var route in context.RouteData.Values)path = path.Replace("{" + route.Key + "}", route.Value.SafeString());return path.ToLower();}/// <summary>/// 根据条件判断是否允许生成HTML/// </summary>/// <param name="relativePath"></param>/// <returns></returns>protected bool IsBuildHtml(string relativePath){if (MinInterval <= 0) return true;var path = Common.GetWebRootPath(relativePath);var fi = new FileInfo(path);if (!fi.Exists) return true;var time = fi.LastWriteTime.DateDiff(DateTime.Now);return time >= TimeSpan.FromSeconds(MinInterval);}/// <summary>/// 写HTML/// </summary>/// <param name="context"></param>/// <param name="stream"></param>protected async Task WriteHtml(ResultExecutingContext context, Stream stream){stream.Position = 0;var responseReader = new StreamReader(stream);var responseContent = await responseReader.ReadToEndAsync();if (string.IsNullOrEmpty(responseContent)) return;var _logger = Web.GetService<ILogger<HtmlStaticAttribute>>();try{var path = Common.GetWebRootPath(RouteReplace(context, Template));FileHelper.Create(path, responseContent);}catch (Exception ex){_logger.LogError(ex, "生成html静态文件失败");}}}
使用方法如下
//在页面顶部增加标签,只要访问该页面就会生成HTML[HtmlStatic(MinInterval = 0, Template = "/static/{page}.html")]public class IndexModel : PageModel{public IndexModel(){}}