Skip to content

Commit 00faa91

Browse files
authored
Merge pull request dotnetcore#28 from jianxuanbing/master
Razor Batch Generate Html File
2 parents 8313efc + 0d8c025 commit 00faa91

File tree

8 files changed

+391
-0
lines changed

8 files changed

+391
-0
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
using Microsoft.AspNetCore.Mvc;
6+
using Microsoft.AspNetCore.Mvc.Infrastructure;
7+
using Util.Helpers;
8+
using Util.Webs.Controllers;
9+
using Util.Webs.Razors;
10+
11+
namespace Util.Samples.Webs.Apis.Tools
12+
{
13+
/// <summary>
14+
/// 初始化Razor控制器
15+
/// </summary>
16+
public class InitRazorController : WebApiControllerBase
17+
{
18+
/// <summary>
19+
/// Razor静态Html生成器
20+
/// </summary>
21+
public IRazorHtmlGenerator RazorHtmlGenerator { get; set; }
22+
23+
/// <summary>
24+
/// 初始化一个<see cref="InitRazorController"/>类型的实例
25+
/// </summary>
26+
/// <param name="razorHtmlGenerator"></param>
27+
public InitRazorController(IRazorHtmlGenerator razorHtmlGenerator)
28+
{
29+
RazorHtmlGenerator = razorHtmlGenerator;
30+
}
31+
32+
/// <summary>
33+
/// 初始化路由
34+
/// </summary>
35+
/// <returns></returns>
36+
[Route("/api/init")]
37+
[HttpPost]
38+
public async Task<IActionResult> Init()
39+
{
40+
await RazorHtmlGenerator.Generate();
41+
return new JsonResult("");
42+
}
43+
}
44+
}

sample/Util.Samples.Webs/Startup.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ public IServiceProvider ConfigureServices( IServiceCollection services ) {
6363
options.IncludeXmlComments( Path.Combine( AppContext.BaseDirectory, "Util.Samples.Webs.xml" ) );
6464
} );
6565

66+
// 添加Razor静态Html生成器
67+
services.AddRazorHtml();
68+
6669
//添加Util基础设施服务
6770
return services.AddUtil();
6871
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using Util.Webs.Razors;
6+
7+
namespace Util.Webs.Extensions
8+
{
9+
/// <summary>
10+
/// 服务扩展
11+
/// </summary>
12+
public static partial class Extensions
13+
{
14+
/// <summary>
15+
/// 注册Razor静态Html生成器
16+
/// </summary>
17+
/// <param name="services"></param>
18+
/// <returns></returns>
19+
public static IServiceCollection AddRazorHtml(this IServiceCollection services)
20+
{
21+
services.AddScoped<IRouteAnalyzer, RouteAnalyzer>();
22+
services.AddScoped<IRazorHtmlGenerator, DefaultRazorHtmlGenerator>();
23+
return services;
24+
}
25+
}
26+
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Microsoft.AspNetCore.Http;
7+
using Microsoft.AspNetCore.Mvc;
8+
using Microsoft.AspNetCore.Mvc.Abstractions;
9+
using Microsoft.AspNetCore.Mvc.ModelBinding;
10+
using Microsoft.AspNetCore.Mvc.Razor;
11+
using Microsoft.AspNetCore.Mvc.Rendering;
12+
using Microsoft.AspNetCore.Mvc.ViewFeatures;
13+
using Microsoft.AspNetCore.Routing;
14+
using Util.Helpers;
15+
using Util.Logs;
16+
using Util.Logs.Extensions;
17+
18+
namespace Util.Webs.Razors
19+
{
20+
/// <summary>
21+
/// Razor静态Html生成器
22+
/// </summary>
23+
public class DefaultRazorHtmlGenerator:IRazorHtmlGenerator
24+
{
25+
private readonly IRouteAnalyzer _routeAnalyzer;
26+
27+
/// <summary>
28+
/// 初始化一个<see cref="DefaultRazorHtmlGenerator"/>类型的实例
29+
/// </summary>
30+
/// <param name="routeAnalyzer">路由分析器</param>
31+
public DefaultRazorHtmlGenerator(IRouteAnalyzer routeAnalyzer)
32+
{
33+
_routeAnalyzer = routeAnalyzer;
34+
}
35+
36+
/// <summary>
37+
/// 生成Html文件
38+
/// </summary>
39+
/// <returns></returns>
40+
public async Task Generate()
41+
{
42+
foreach (var routeInformation in _routeAnalyzer.GetAllRouteInformations())
43+
{
44+
// 跳过API的处理
45+
if (routeInformation.Path.StartsWith("/api"))
46+
{
47+
continue;
48+
}
49+
await WriteViewToFileAsync(routeInformation);
50+
}
51+
}
52+
53+
/// <summary>
54+
/// 渲染视图为字符串
55+
/// </summary>
56+
/// <param name="info">路由信息</param>
57+
/// <returns></returns>
58+
public async Task<string> RenderToStringAsync(RouteInformation info)
59+
{
60+
var razorViewEngine = Ioc.Create<IRazorViewEngine>();
61+
var tempDataProvider = Ioc.Create<ITempDataProvider>();
62+
var serviceProvider = Ioc.Create<IServiceProvider>();
63+
64+
var routeData = new RouteData();
65+
if (!info.AreaName.IsEmpty())
66+
{
67+
routeData.Values.Add("area", info.AreaName);
68+
}
69+
70+
if (!info.ControllerName.IsEmpty())
71+
{
72+
routeData.Values.Add("controller", info.ControllerName);
73+
}
74+
75+
if (!info.ActionName.IsEmpty())
76+
{
77+
routeData.Values.Add("action", info.ActionName);
78+
}
79+
80+
var httpContext = new DefaultHttpContext { RequestServices = serviceProvider };
81+
var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor());
82+
83+
var viewResult = razorViewEngine.FindView(actionContext, info.ActionName, true);
84+
if (!viewResult.Success)
85+
{
86+
throw new InvalidOperationException($"找不到视图模板 {info.ActionName}");
87+
}
88+
89+
using (var stringWriter = new StringWriter())
90+
{
91+
var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary());
92+
var viewContext = new ViewContext(actionContext, viewResult.View, viewDictionary, new TempDataDictionary(actionContext.HttpContext, tempDataProvider), stringWriter, new HtmlHelperOptions());
93+
await viewResult.View.RenderAsync(viewContext);
94+
return stringWriter.ToString();
95+
}
96+
}
97+
98+
/// <summary>
99+
/// 将视图写入文件
100+
/// </summary>
101+
/// <param name="info">路由信息</param>
102+
/// <returns></returns>
103+
public async Task WriteViewToFileAsync(RouteInformation info)
104+
{
105+
try
106+
{
107+
var html = await RenderToStringAsync(info);
108+
if (string.IsNullOrWhiteSpace(html))
109+
return;
110+
111+
var path = Util.Helpers.Common.GetPhysicalPath(string.IsNullOrWhiteSpace(info.FilePath) ? GetPath(info) : info.FilePath);
112+
var directory = System.IO.Path.GetDirectoryName(path);
113+
if (string.IsNullOrWhiteSpace(directory))
114+
return;
115+
if (Directory.Exists(directory) == false)
116+
Directory.CreateDirectory(directory);
117+
File.WriteAllText(path, html);
118+
}
119+
catch (Exception ex)
120+
{
121+
ex.Log(Log.GetLog().Caption("生成html静态文件失败"));
122+
}
123+
}
124+
125+
protected virtual string GetPath(RouteInformation info)
126+
{
127+
var area = info.AreaName.SafeString();
128+
var controller = info.ControllerName.SafeString();
129+
var action = info.ActionName.SafeString();
130+
var path = info.TemplatePath.Replace("{area}", area).Replace("{controller}", controller).Replace("{action}", action);
131+
return path.ToLower();
132+
}
133+
}
134+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using System.Threading.Tasks;
5+
6+
namespace Util.Webs.Razors
7+
{
8+
/// <summary>
9+
/// Razor Html生成器
10+
/// </summary>
11+
public interface IRazorHtmlGenerator
12+
{
13+
/// <summary>
14+
/// 生成Html文件
15+
/// </summary>
16+
/// <returns></returns>
17+
Task Generate();
18+
}
19+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace Util.Webs.Razors
6+
{
7+
/// <summary>
8+
/// 路由分析器
9+
/// </summary>
10+
public interface IRouteAnalyzer
11+
{
12+
/// <summary>
13+
/// 获取所有路由信息
14+
/// </summary>
15+
/// <returns></returns>
16+
IEnumerable<RouteInformation> GetAllRouteInformations();
17+
}
18+
}

src/Util.Webs/Razors/RouteAnalyzer.cs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Reflection;
4+
using System.Text;
5+
using Microsoft.AspNetCore.Mvc.Controllers;
6+
using Microsoft.AspNetCore.Mvc.Infrastructure;
7+
using Microsoft.AspNetCore.Mvc.RazorPages;
8+
using Util.Webs.Filters;
9+
10+
namespace Util.Webs.Razors
11+
{
12+
/// <summary>
13+
/// 路由分析器
14+
/// </summary>
15+
public class RouteAnalyzer:IRouteAnalyzer
16+
{
17+
/// <summary>
18+
/// 操作描述集合提供程序
19+
/// </summary>
20+
private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider;
21+
22+
/// <summary>
23+
/// 初始化一个<see cref="RouteAnalyzer"/>类型的实例
24+
/// </summary>
25+
/// <param name="actionDescriptorCollectionProvider">操作描述集合提供程序</param>
26+
public RouteAnalyzer(IActionDescriptorCollectionProvider actionDescriptorCollectionProvider)
27+
{
28+
_actionDescriptorCollectionProvider = actionDescriptorCollectionProvider;
29+
}
30+
31+
/// <summary>
32+
/// 获取所有路由信息
33+
/// </summary>
34+
/// <returns></returns>
35+
public IEnumerable<RouteInformation> GetAllRouteInformations()
36+
{
37+
List<RouteInformation> list=new List<RouteInformation>();
38+
39+
var actionDescriptors = this._actionDescriptorCollectionProvider.ActionDescriptors.Items;
40+
foreach (var actionDescriptor in actionDescriptors)
41+
{
42+
RouteInformation info=new RouteInformation();
43+
44+
if (actionDescriptor.RouteValues.ContainsKey("area"))
45+
{
46+
info.AreaName = actionDescriptor.RouteValues["area"];
47+
}
48+
49+
// Razor页面路径以及调用
50+
if (actionDescriptor is PageActionDescriptor pageActionDescriptor)
51+
{
52+
info.Path = pageActionDescriptor.ViewEnginePath;
53+
info.Invocation = pageActionDescriptor.RelativePath;
54+
}
55+
56+
// 路由属性路径
57+
if (actionDescriptor.AttributeRouteInfo != null)
58+
{
59+
info.Path = $"/{actionDescriptor.AttributeRouteInfo.Template}";
60+
}
61+
62+
// Controller/Action 的路径以及调用
63+
if (actionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
64+
{
65+
if (info.Path.IsEmpty())
66+
{
67+
info.Path =
68+
$"/{controllerActionDescriptor.ControllerName}/{controllerActionDescriptor.ActionName}";
69+
}
70+
71+
var controllerHtmlAttribute = controllerActionDescriptor.ControllerTypeInfo.GetCustomAttribute<HtmlAttribute>();
72+
73+
if (controllerHtmlAttribute != null)
74+
{
75+
info.FilePath = controllerHtmlAttribute.Path;
76+
info.TemplatePath = controllerHtmlAttribute.Template;
77+
}
78+
79+
var htmlAttribute = controllerActionDescriptor.MethodInfo.GetCustomAttribute<HtmlAttribute>();
80+
81+
if (htmlAttribute != null)
82+
{
83+
info.FilePath = htmlAttribute.Path;
84+
info.TemplatePath = htmlAttribute.Template;
85+
}
86+
87+
info.ControllerName = controllerActionDescriptor.ControllerName;
88+
info.ActionName = controllerActionDescriptor.ActionName;
89+
info.Invocation = $"{controllerActionDescriptor.ControllerName}Controller.{controllerActionDescriptor.ActionName}";
90+
}
91+
92+
info.Invocation += $"({actionDescriptor.DisplayName})";
93+
94+
list.Add(info);
95+
}
96+
97+
return list;
98+
}
99+
}
100+
}

0 commit comments

Comments
 (0)