Skip to content

Commit 0f2d784

Browse files
committed
feat: allow extensionless HTML content access
1 parent 0cceb23 commit 0f2d784

File tree

1 file changed

+42
-0
lines changed

1 file changed

+42
-0
lines changed

src/Docfx.App/RunServe.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics;
5+
using System.Diagnostics.CodeAnalysis;
56
using System.Runtime.InteropServices;
67
using Docfx.Common;
78
using Docfx.Plugins;
89
using Microsoft.AspNetCore.Builder;
910
using Microsoft.AspNetCore.Hosting;
11+
using Microsoft.AspNetCore.Http;
1012
using Microsoft.Extensions.FileProviders;
1113
using Microsoft.Extensions.Hosting;
1214
using Microsoft.Extensions.Logging;
1315

16+
#nullable enable
17+
1418
namespace Docfx;
1519

1620
/// <summary>
@@ -44,6 +48,7 @@ public static void Exec(string folder, string host, int? port, bool openBrowser,
4448
Console.WriteLine($"Serving \"{folder}\" on {url}");
4549
Console.WriteLine("Press Ctrl+C to shut down");
4650
using var app = builder.Build();
51+
app.UseExtensionlessHtmlUrl();
4752
app.UseServe(folder);
4853

4954
if (openBrowser || !string.IsNullOrEmpty(openFile))
@@ -161,4 +166,41 @@ private static void LaunchBrowser(string url)
161166
Logger.LogError($"Could not launch the browser process. with error - {ex.Message}");
162167
}
163168
}
169+
170+
/// <summary>
171+
/// Enable HTML content access with extensionless URL.
172+
/// This extension method must be called before `UseFileServer` or `UseStaticFiles`.
173+
/// </summary>
174+
private static IApplicationBuilder UseExtensionlessHtmlUrl(this WebApplication app)
175+
{
176+
// Configure middleware that rewrite extensionless url to physical HTML file path.
177+
return app.Use(async (context, next) =>
178+
{
179+
if (IsGetOrHeadMethod(context.Request.Method)
180+
&& TryResolveHtmlFilePath(context.Request.Path, out var htmlFilePath))
181+
{
182+
context.Request.Path = htmlFilePath;
183+
}
184+
185+
await next();
186+
});
187+
188+
static bool IsGetOrHeadMethod(string method) => HttpMethods.IsGet(method) || HttpMethods.IsHead(method);
189+
190+
// Try to resolve HTML file path.
191+
bool TryResolveHtmlFilePath(PathString pathString, [NotNullWhen(true)] out string? htmlPath)
192+
{
193+
var path = pathString.Value;
194+
if (!string.IsNullOrEmpty(path) && !Path.HasExtension(path) && !path.EndsWith('/'))
195+
{
196+
htmlPath = $"{path}.html";
197+
var fileInfo = app.Environment.WebRootFileProvider.GetFileInfo(htmlPath);
198+
if (fileInfo != null)
199+
return true;
200+
}
201+
202+
htmlPath = null;
203+
return false;
204+
}
205+
}
164206
}

0 commit comments

Comments
 (0)