ASP.NET Tips #006 - ASP.NET Core で Server.MapPath() 相当の拡張メソッドを作る

サーバー上にファイルが存在するかどうかで処理や表示を切り替えたりする際には、仮想パスで指定されるとサーバー上の物理パスに変換する必要があったりします。
しかし、 ASP.NET Core では Server.MapPath() が無いため、簡単に変換する事ができません。

wwwroot までの物理パスを取得するには、コントローラーのコンストラクターで HostEnvironment を受け取っておき、 WebRootPath を参照します。

ファイルが存在するかどうかをチェックする場合は、この WebRootPath にディレクトリー名やファイル名を連結したパスを評価しなければなりません。

そこで、 HostEnvironment にアプリケーションルートからの仮想パスを物理パスに変換する拡張メソッドを用意したいと思います。

まず、プロジェクト直下に Extensions フォルダーを作成し、「HostEnvironmentExtension.cs」クラスを追加します。

指定された「~/pdf/file.pdf」「/appsettings.json」の様なファイルパスを「¥」で区切られた物理パスに置き換える GetPhysicalPath() メソッドを用意します。
内部では Regex.Replace() でファイルパスの先頭を置き換え、 Replace() で全ての「/」を「¥」に置き換えています。

ContentRootPath からの物理パスを取得する MapContentRootPath() メソッドと、WebRootPath からの物理パスを取得する MapWebRootPath() メソッドの2つを、 IWebHostEnvironment の拡張メソッドとして定義して、アプリケーションルートからのパスと GetPhysicalPath() で変換したファイルパスを Path.Combine() で結合して返すようにします。

~/Extensions/HostEnvironmentExtension.cs
using System.IO;
using System.Text.RegularExpressions;

namespace Microsoft.AspNetCore.Hosting
{
	public static class HostEnvironmentExtension
	{
		/// <summary>
		/// ファイルの相対パスを物理パス区切りに変換して ContentRootPath と連結して返します
		/// </summary>
		/// <param name="env">IWebHostEnvironment を拡張します</param>
		/// <param name="filePath">ファイルの相対パス</param>
		/// <returns></returns>
		public static string MapContentRootPath(this IWebHostEnvironment env, string filePath)
		{
			var path = GetPhysicalPath(filePath);
			var result = Path.Combine(env.ContentRootPath, path);
			return result;
		}

		/// <summary>
		/// ファイルの相対パスを物理パス区切りに変換して WebRootPath と連結して返します
		/// </summary>
		/// <param name="env">IWebHostEnvironment を拡張します</param>
		/// <param name="filePath">ファイルの相対パス</param>
		/// <returns></returns>
		public static string MapWebRootPath(this IWebHostEnvironment env, string filePath)
		{
			var path = GetPhysicalPath(filePath);
			var result = Path.Combine(env.WebRootPath, path);
			return result;
		}

		/// <summary>
		/// 仮想ファイルパスを物理ファイルパスに変換します
		/// </summary>
		/// <param name="virtualPath">仮想ファイルパス</param>
		/// <returns></returns>
		private static string GetPhysicalPath(string virtualPath)
		{
			var result = Regex.Replace(virtualPath, @"^~/|^/", string.Empty).Replace("/", @"\");
			return result;
		}
	}
}

※クラスの namespace を 「Microsoft.AspNetCore.Hosting」にする事で、拡張メソッドを利用する際に using を指定しなくても拡張メソッドを参照できる様にしています。

あとはコントローラーのコンストラクターで受け取っておいた IWebHostEnvironment _env から拡張メソッドを呼び出し、仮想ファイルパスを渡せば、サーバー上の物理パスを取得できるので、 File.Exists() で実際に存在するかどうかチェックします。

~/Controllers/HomeController.cshtml
public class HomeController : Controller
{
	private readonly IWebHostEnvironment _env;

	public HomeController(IWebHostEnvironment env)
	{
		_env = env;
	}

	public IActionResult Index()
	{
		var target1 = "/appsettings.json";
		var path1 = _env.MapContentRootPath(target1);
		ViewBag.Result1 = System.IO.File.Exists(path1) ? "存在します" : "存在しません";

		var target2 = "~/pdf/sample.pdf";
		var path2 = _env.MapWebRootPath(target2);
		ViewBag.Result2 = System.IO.File.Exists(path2) ? "存在します" : "存在しません";

		return View();
	}
試作環境
  • Windows 10 (20H2)
  • Visual Studio 2019 (v16.8.4)
  • .NET SDK 5.0.102
  • ASP.NET Core 5.0 / MVC