实现 Razor生成NG Html文件

This commit is contained in:
jian玄冰
2018-06-13 18:51:25 +08:00
parent 8313efca77
commit 0d8c025bdf
8 changed files with 391 additions and 0 deletions
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Util.Helpers;
using Util.Webs.Controllers;
using Util.Webs.Razors;
namespace Util.Samples.Webs.Apis.Tools
{
/// <summary>
/// 初始化Razor控制器
/// </summary>
public class InitRazorController : WebApiControllerBase
{
/// <summary>
/// Razor静态Html生成器
/// </summary>
public IRazorHtmlGenerator RazorHtmlGenerator { get; set; }
/// <summary>
/// 初始化一个<see cref="InitRazorController"/>类型的实例
/// </summary>
/// <param name="razorHtmlGenerator"></param>
public InitRazorController(IRazorHtmlGenerator razorHtmlGenerator)
{
RazorHtmlGenerator = razorHtmlGenerator;
}
/// <summary>
/// 初始化路由
/// </summary>
/// <returns></returns>
[Route("/api/init")]
[HttpPost]
public async Task<IActionResult> Init()
{
await RazorHtmlGenerator.Generate();
return new JsonResult("");
}
}
}
+3
View File
@@ -63,6 +63,9 @@ namespace Util.Samples.Webs {
options.IncludeXmlComments( Path.Combine( AppContext.BaseDirectory, "Util.Samples.Webs.xml" ) );
} );
// 添加Razor静态Html生成器
services.AddRazorHtml();
//添加Util基础设施服务
return services.AddUtil();
}
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using Util.Webs.Razors;
namespace Util.Webs.Extensions
{
/// <summary>
/// 服务扩展
/// </summary>
public static partial class Extensions
{
/// <summary>
/// 注册Razor静态Html生成器
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddRazorHtml(this IServiceCollection services)
{
services.AddScoped<IRouteAnalyzer, RouteAnalyzer>();
services.AddScoped<IRazorHtmlGenerator, DefaultRazorHtmlGenerator>();
return services;
}
}
}
@@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;
using Util.Helpers;
using Util.Logs;
using Util.Logs.Extensions;
namespace Util.Webs.Razors
{
/// <summary>
/// Razor静态Html生成器
/// </summary>
public class DefaultRazorHtmlGenerator:IRazorHtmlGenerator
{
private readonly IRouteAnalyzer _routeAnalyzer;
/// <summary>
/// 初始化一个<see cref="DefaultRazorHtmlGenerator"/>类型的实例
/// </summary>
/// <param name="routeAnalyzer">路由分析器</param>
public DefaultRazorHtmlGenerator(IRouteAnalyzer routeAnalyzer)
{
_routeAnalyzer = routeAnalyzer;
}
/// <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 = Util.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();
}
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Util.Webs.Razors
{
/// <summary>
/// Razor Html生成器
/// </summary>
public interface IRazorHtmlGenerator
{
/// <summary>
/// 生成Html文件
/// </summary>
/// <returns></returns>
Task Generate();
}
}
+18
View File
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Util.Webs.Razors
{
/// <summary>
/// 路由分析器
/// </summary>
public interface IRouteAnalyzer
{
/// <summary>
/// 获取所有路由信息
/// </summary>
/// <returns></returns>
IEnumerable<RouteInformation> GetAllRouteInformations();
}
}
+100
View File
@@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Util.Webs.Filters;
namespace Util.Webs.Razors
{
/// <summary>
/// 路由分析器
/// </summary>
public class RouteAnalyzer:IRouteAnalyzer
{
/// <summary>
/// 操作描述集合提供程序
/// </summary>
private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider;
/// <summary>
/// 初始化一个<see cref="RouteAnalyzer"/>类型的实例
/// </summary>
/// <param name="actionDescriptorCollectionProvider">操作描述集合提供程序</param>
public RouteAnalyzer(IActionDescriptorCollectionProvider actionDescriptorCollectionProvider)
{
_actionDescriptorCollectionProvider = actionDescriptorCollectionProvider;
}
/// <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<HtmlAttribute>();
if (controllerHtmlAttribute != null)
{
info.FilePath = controllerHtmlAttribute.Path;
info.TemplatePath = controllerHtmlAttribute.Template;
}
var htmlAttribute = controllerActionDescriptor.MethodInfo.GetCustomAttribute<HtmlAttribute>();
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;
}
}
}
+47
View File
@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Util.Webs.Razors
{
/// <summary>
/// 路由信息
/// </summary>
public class RouteInformation
{
/// <summary>
/// 区域名称
/// </summary>
public string AreaName { get; set; }
/// <summary>
/// 控制器名称
/// </summary>
public string ControllerName { get; set; }
/// <summary>
/// 操作名称
/// </summary>
public string ActionName { get; set; }
/// <summary>
/// Razor页面路径
/// </summary>
public string Path { get; set; }
/// <summary>
/// 调用方法
/// </summary>
public string Invocation { get; set; }
/// <summary>
/// 文件路径
/// </summary>
public string FilePath { get; set; }
/// <summary>
/// 模板路径
/// </summary>
public string TemplatePath { get; set; }
}
}