在傳統 ASP.NET 的年代,我們別無選擇,寫好的 ASP.NET 應用程式只能 Host 在 IIS 上執行,其中虛擬目錄
的服務是由 StaticFile
這個 HTTP Handler 來負責處理。
而 ASP.NET Core 內建就有 Kestrel 這個輕量化的網頁伺服器,不需要再依賴 IIS,但是脫離 IIS 之後,我們要怎麼設定虛擬目錄?
ASP.NET Core 已經不再依賴 IIS,那麼 IIS 上內建一大堆的 HTTP Handlers 及 HTTP Modules 也跟 ASP.NET Core 沒啥關係了,而這些 HTTP Handlers 及 HTTP Modules 在 ASP.NET Core 統一由 Middleware 來負責。
像是我們需要支援靜態檔案,在 ASP.NET Core 就使用 app.UseStaticFiles() 這個 Middleware,但是預設只對 Web Root(wwwroot)底下的靜態檔案有作用,虛擬目錄它就沒有支援,那怎麼辦? 那就自己寫一個。
我個人覺得,對比開發傳統 ASP.NET 的 HTTP Handler 或 HTTP Module,開發 ASP.NET Core 的 Middleware 真的容易多了,接下來我們就以虛擬目錄為例,使用擴充方法寫一個支援虛擬目錄的 Middleware,原始碼如下,說明就在註解中。
public static class IApplicationBuilderExtension
{
public static void UseVirtualDirectory(this IApplicationBuilder me, string virtualDirectory, string physicalDirectory)
{
me.Use(
async (ctx, next) =>
{
// 比對 Request Path
var match = Regex.Match(ctx.Request.Path.Value, $"^/{virtualDirectory}/(.+)", RegexOptions.IgnoreCase);
if (match.Success)
{
// 用實體目錄路錄建立 FileProvider
var fileProvider = new PhysicalFileProvider(physicalDirectory);
// 使用相對路徑取得 FileInfo
var fileInfo = fileProvider.GetFileInfo(match.Groups[1].Value);
if (fileInfo.Exists)
{
// 取得預設的 ContentType Mappings
var contentTypeProvider = new FileExtensionContentTypeProvider();
// 依據檔案的副檔名取得 ContentType
if (!contentTypeProvider.TryGetContentType(fileInfo.PhysicalPath, out var contentType))
{
contentType = "application/octet-stream";
}
ctx.Response.ContentType = contentType;
// 回應靜態檔案內容
await ctx.Response.SendFileAsync(fileInfo);
}
else
{
await next();
}
}
else
{
await next();
}
});
}
}
接著在 Startup.cs 的 Configure() 方法中加入 app.UseVirtualDirectory()
方法,並且指定虛擬目錄名稱及對應的實體路徑。
測試結果
除此之外,如果我們有在 ASP.NET Core 應用程式前面擋一層 IIS、Nginx、Apache、...等的網頁伺服器的話,把虛擬目錄設定在這些網頁伺服器上或許會比寫在 ASP.NET Core 更適合,以上分享給大家。