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

一、前言

最近做项目的时候,使用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控制器配置
    640?wx_fmt=png

  • Startup配置
    640?wx_fmt=png

  • 一次性生成方式,调用一次接口即可
    640?wx_fmt=png


五、源码地址

Util: https://github.com/dotnetcore/Util

Bing.NetCore: https://github.com/bing-framework/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

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


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/321135.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NEERC2017 Archery Tournament 线段树 新套路

题目链接 http://codeforces.com/gym/101630/attachments/download/6401/20172018-acmicpc-northeastern-european-regional-contest-neerc-17-en.pdf 题意 给出一些圆&#xff0c;这些圆圆心在x轴上方&#xff0c;且与x轴相切&#xff0c;任意时刻&#xff0c;不存在图上的…

P2178-[NOI2015]品酒大会【SA,并查集】

正题 题目链接:https://www.luogu.com.cn/problem/P2178 题目大意 长度为nnn的字符串&#xff0c;每个位置有一个权值。 如果对于q,pq,pq,p有sq,qr−1sp,pr−1s_{q,qr-1}s_{p,pr-1}sq,qr−1​sp,pr−1​那么就称q,pq,pq,p为rrr相似。 对于每个rrr求有多少对q,pq,pq,p是rrr相似…

【Floyed】【最短路】商店选址问题(ssl 1760)

商店选址问题 ssl 1760 题目大意&#xff1a; 有一些点&#xff0c;在一个点上按医院&#xff0c;有一个值就是这个点到其他点的最短路之和&#xff0c;问这个值最小是多少 原题&#xff1a; Description 给出一个城市的地图&#xff08;用邻接矩阵表示&#xff09;&…

一文看懂.NET的各种变体

曾几何时&#xff0c;我们只有一个.NET&#xff0c;叫作.NET Framework。如果想要开发.NET应用程序&#xff0c;只要使用.NET Framework即可&#xff0c;非常简单。几年之后&#xff0c;出现了.NET变种的寒武纪大爆发&#xff08;我们称之为“.NET大爆炸”&#xff09;&#xf…

Codeforces Gym - 100917 部分题解

A.Abstract Picture Gym - 100917A 分析&#xff1a;由于最后刷的一笔肯定使得某一行或者是某一列均为相同的颜色。 因此我们可以在一开始找到所有的行或者列&#xff0c;他们的颜色全都一样&#xff0c;把这样的行或列加入到队列里面去。 我们处理在队列里面的行或者列&…

【最短路】【Floyed】医院设置(ssl 1614)

医院设置 ssl 1614 题目大意&#xff1a; 有n个点&#xff0c;在一个点上安医院&#xff0c;使这个点到其他点的最短路之和最小 原题&#xff1a; Description 设有一棵二叉树&#xff08;如右图&#xff09;。其中&#xff0c;圈中的数字表示结点中居民的人口。圈边上数…

牛客练习赛69C-旅行【结论,最大生成树】

正题 题目链接:https://ac.nowcoder.com/acm/contest/7329/C 题目大意 disx,ydis_{x,y}disx,y​表示x,yx,yx,y的所有路径的最短的边的最大值。 求一个1∼n1\sim n1∼n的排列&#xff0c;使得∑i2ndisi,i−1\sum_{i2}^ndis_{i,i-1}∑i2n​disi,i−1​最大 解题思路 首先一定是…

拓展 NLog 优雅的输送日志到 Logstash

在上上篇博客通过对aspnetcore启动前配置做了一些更改&#xff0c;以及对nlog进行了自定义字段&#xff0c;可以把请求记录输送到mysql&#xff0c;正式情况可能不会这么部署。因为近期也在学习elk&#xff0c;所以就打算做一个实例&#xff0c;结合nlog把日志输送到logstash&a…

曼哈顿距离与切比雪夫距离的转化及prufer序列

目录 曼哈顿距离与切比雪夫距离的相互转化prufer序列 1. 曼哈顿距离 与 切比雪夫距离 的相互转化 曼哈顿距离 |x1−x2||y1−y2|max(x1−x2y1−y2,x1−x2−y1y2,−x1x2y1−y2,−x1x2−y1y2)|x1−x2||y1−y2|max(x1−x2y1−y2,x1−x2−y1y2,−x1x2y1−y2,−x1x2−y1y2)|x_1 - x…

初一模拟赛总结(2019.3.9)

成绩&#xff1a; rank算上了其他大佬 rankrankranknamenamenamescorescorescoreT1T1T1T2T2T2T3T3T3T4T4T4222lyflyflyf230230230100100100303030000100100100333hkyhkyhky909090000000000909090444fyfyfy808080000606060000202020444whdwhdwhd808080202020606060000000666lth…

牛客练习赛69E-子串【树状数组】

正题 题目链接:https://ac.nowcoder.com/acm/contest/7329/E 题目大意 给出一个nnn的排列&#xff0c;求有多少个区间[l,r][l,r][l,r]使得最大值是rrr&#xff0c;最小值是lll。 解题思路 首先对于一个位置的值作为左端点和右端点都有一段合法区间&#xff08;到左边第一个比…

[译]如何在.NET Core中使用System.Drawing?

你大概知道System.Drawing&#xff0c;它是一个执行图形相关任务的流行的API&#xff0c;同时它也不属于.NET Core的一部分。最初是把.NET Core作为云端框架设计的&#xff0c;它不包含非云端相关API。另一方面&#xff0c;.NET Core是跨平台框架&#xff0c;它不包含任何操作系…

【dfs】棋盘变幻

棋盘变幻 题目大意&#xff1a; 有一些棋子&#xff08;黑/白&#xff09;&#xff0c;可以将某一列的棋子颜色反转&#xff0c;要黑棋尽可能小 原题&#xff1a; 题目描述 小G在一个n*m的棋盘上随意放上了一些黑色的棋子&#xff0c;然后又在剩下所有没有放棋子的格子里放…

组合数学与数论函数专题

组合数学专题 专题简介 本专题包含了一些组合数学中常见的套路和方法,如拉格朗日插值,动态规划,容斥原理,狄利克雷卷积,线性筛,杜教筛 等等. 目录 2018 四川省赛GRISAIA (数论分块)HDU 6428 Calculate (狄利克雷卷积,线性筛)BZOJ4559 成绩比较 (动态规划,拉格朗日插值)BZOJ…

关于Visual Studio 2019的前期详情

近日&#xff0c;来自微软公司的 John Montgomery 正式宣布&#xff0c;Visual Studio 2019已进入开发阶段。Montgomery 表示&#xff0c;之所以选择在这个时间点公开这个消息&#xff0c;是因为微软准备在 GitHub 上公开可见的项目&#xff08;包括 .NET 和 Roslyn&#xff09…

nssl1522-简单数数题【dp】

正题 题目大意 nnn个数的一个集合&#xff0c;求一个有多少个子集使得这个子集的所有子集的权值和的和是mmm的倍数 解题思路 考虑dp&#xff0c;选中集合中每一个数的贡献次数是2∣S∣−12^{|S|-1}2∣S∣−1&#xff0c;设fi,j,kf_{i,j,k}fi,j,k​表示选到第iii&#xff0c;现…

蛋糕店

蛋糕店 题目大意&#xff1a; 有n个人&#xff0c;让他们排队&#xff0c;每个人都有一个愤怒值&#xff0c;总值为每个人的愤怒值乘上&#xff08;他在队列中的位置-1&#xff09;加在一起 原题&#xff1a; 题目描述 最近小G新开了一家蛋糕店。开业第一天&#xff0c;一…

开源的,跨平台的.NET机器学习框架ML.NET

微软在Build 2018大会上推出的一款面向.NET开发人员的开源&#xff0c;跨平台机器学习框架ML.NET。 ML.NET将允许.NET开发人员开发他们自己的模型&#xff0c;并将自定义ML集成到他们的应用程序中&#xff0c;而无需事先掌握开发或调整机器学习模型的专业知识。在采用通用机器学…

nssl1519-背包签到题【数论】

正题 题目链接:http://10.156.31.134/contestnew.aspx?cid189 题目大意 nnn种物品&#xff0c;每种aia_iai​个。放进kkk个有序盒子求方案数&#xff08;可以不放完&#xff09; 解题思路 不放完其实就是k1k1k1个盒子&#xff0c;插板法求CkaiaiC_{ka_i}^{a_i}Ckai​ai​​&…

【dfs】相似度

相似度 题目大意&#xff1a; 有两个图&#xff0c;问他们的相似度是多少&#xff08;相似度为连接两个点的相样线的条数&#xff09; 原题&#xff1a; 题目描述 小G通过摆放一些城市和道路构成了一个世界地图。趁着小G出去玩的时候&#xff0c;大G把小G的世界地图上的城…