[解決済み] MVCでデフォルトルート(エリア)を設定する方法
質問
これは以前にも質問されたことがあるのですが、確かな解決策がないのです。そこで、私自身のため、そしてこれが役に立つと思う他の人のために、この質問をします。
MVC2 (ASP.NET) では、誰かがウェブサイトにナビゲートすると、指定されたデフォルトの領域があるようにしたいです。したがって、私のサイトにナビゲートすると、AreaZ の ControllerX ActionY に送られるはずです。
Global.asaxで次のルートを使用します。
routes.MapRoute(
"Area",
"",
new { area = "AreaZ", controller = "ControllerX ", action = "ActionY " }
);
これで、正しいページを提供しようとするように動作します。しかし、MVC は Area フォルダではなく、サイトのルートにある View を探そうとします。
これを解決する方法はありますか。
EDIT
解決策があります。それは、ControllerX, ActionYで、ビューのフルパスを返すことです。少しハックしていますが、動作しています。しかし、私はより良いソリューションがあることを期待しています。
public ActionResult ActionY()
{
return View("~/Areas/AreaZ/views/ActionY.aspx");
}
編集する
これは、ページのHTMLアクションリンクがある場合にも問題になります。領域が設定されていない場合、アクションリンクは空白で出力されます。
これはすべてデザインなのでしょうか、それとも欠陥なのでしょうか?
どのように解決するのですか?
これは私が興味を持ったもので、ようやく調べる機会がありました。 他の人たちは、どうやら、この問題が ビューを見つける の問題ではなく ルーティング そのものではなく、質問のタイトルがルーティングに関するものであることを示しているからでしょう。
いずれにせよ、これはViewに関連する問題なので、あなたが望むものを得るための唯一の方法は
デフォルトのビューエンジンをオーバーライドする
. 通常、これを行う場合、ビューエンジンを切り替える(SparkやNHamlなどに)という単純な目的のために行います。 この場合、オーバーライドする必要があるのはビュー作成ロジックではなく
FindPartialView
と
FindView
メソッドを
VirtualPathProviderViewEngine
クラスで使用されます。
これらのメソッドが実際に仮想的であることを幸運に感謝することができます。
VirtualPathProviderViewEngine
は
アクセス可能な
- プライベートなものであり、そのため
は非常に
なぜなら、ロケーションキャッシュとロケーションフォーマットをうまく使うには、基本的にすでに書かれているコードの半分を書き直さなければならないからです。 Reflector をいくつか調査した後、私は最終的に動作するソリューションを考え出すことに成功しました。
ここで私が行ったことは、まず抽象的な
AreaAwareViewEngine
から直接派生した
VirtualPathProviderViewEngine
ではなく
WebFormViewEngine
. これは、もしあなたがSparkのビューを作成したい場合(あるいは何でも)、このクラスを基本型として使用できるようにしたものです。
以下のコードはかなり長ったらしいので、実際に何をするのか簡単にまとめます。
{2}
をエリア名に対応するロケーションフォーマットに入れることができます。
{1}
がコントローラ名に対応するのと同じです。 これだけです。そのためにこのようなコードを書かなければならなかったのです。
BaseAreaAwareViewEngine.cs
public abstract class BaseAreaAwareViewEngine : VirtualPathProviderViewEngine
{
private static readonly string[] EmptyLocations = { };
public override ViewEngineResult FindView(
ControllerContext controllerContext, string viewName,
string masterName, bool useCache)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (string.IsNullOrEmpty(viewName))
{
throw new ArgumentNullException(viewName,
"Value cannot be null or empty.");
}
string area = getArea(controllerContext);
return FindAreaView(controllerContext, area, viewName,
masterName, useCache);
}
public override ViewEngineResult FindPartialView(
ControllerContext controllerContext, string partialViewName,
bool useCache)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (string.IsNullOrEmpty(partialViewName))
{
throw new ArgumentNullException(partialViewName,
"Value cannot be null or empty.");
}
string area = getArea(controllerContext);
return FindAreaPartialView(controllerContext, area,
partialViewName, useCache);
}
protected virtual ViewEngineResult FindAreaView(
ControllerContext controllerContext, string areaName, string viewName,
string masterName, bool useCache)
{
string controllerName =
controllerContext.RouteData.GetRequiredString("controller");
string[] searchedViewPaths;
string viewPath = GetPath(controllerContext, ViewLocationFormats,
"ViewLocationFormats", viewName, controllerName, areaName, "View",
useCache, out searchedViewPaths);
string[] searchedMasterPaths;
string masterPath = GetPath(controllerContext, MasterLocationFormats,
"MasterLocationFormats", masterName, controllerName, areaName,
"Master", useCache, out searchedMasterPaths);
if (!string.IsNullOrEmpty(viewPath) &&
(!string.IsNullOrEmpty(masterPath) ||
string.IsNullOrEmpty(masterName)))
{
return new ViewEngineResult(CreateView(controllerContext, viewPath,
masterPath), this);
}
return new ViewEngineResult(
searchedViewPaths.Union<string>(searchedMasterPaths));
}
protected virtual ViewEngineResult FindAreaPartialView(
ControllerContext controllerContext, string areaName,
string viewName, bool useCache)
{
string controllerName =
controllerContext.RouteData.GetRequiredString("controller");
string[] searchedViewPaths;
string partialViewPath = GetPath(controllerContext,
ViewLocationFormats, "PartialViewLocationFormats", viewName,
controllerName, areaName, "Partial", useCache,
out searchedViewPaths);
if (!string.IsNullOrEmpty(partialViewPath))
{
return new ViewEngineResult(CreatePartialView(controllerContext,
partialViewPath), this);
}
return new ViewEngineResult(searchedViewPaths);
}
protected string CreateCacheKey(string prefix, string name,
string controller, string area)
{
return string.Format(CultureInfo.InvariantCulture,
":ViewCacheEntry:{0}:{1}:{2}:{3}:{4}:",
base.GetType().AssemblyQualifiedName,
prefix, name, controller, area);
}
protected string GetPath(ControllerContext controllerContext,
string[] locations, string locationsPropertyName, string name,
string controllerName, string areaName, string cacheKeyPrefix,
bool useCache, out string[] searchedLocations)
{
searchedLocations = EmptyLocations;
if (string.IsNullOrEmpty(name))
{
return string.Empty;
}
if ((locations == null) || (locations.Length == 0))
{
throw new InvalidOperationException(string.Format("The property " +
"'{0}' cannot be null or empty.", locationsPropertyName));
}
bool isSpecificPath = IsSpecificPath(name);
string key = CreateCacheKey(cacheKeyPrefix, name,
isSpecificPath ? string.Empty : controllerName,
isSpecificPath ? string.Empty : areaName);
if (useCache)
{
string viewLocation = ViewLocationCache.GetViewLocation(
controllerContext.HttpContext, key);
if (viewLocation != null)
{
return viewLocation;
}
}
if (!isSpecificPath)
{
return GetPathFromGeneralName(controllerContext, locations, name,
controllerName, areaName, key, ref searchedLocations);
}
return GetPathFromSpecificName(controllerContext, name, key,
ref searchedLocations);
}
protected string GetPathFromGeneralName(ControllerContext controllerContext,
string[] locations, string name, string controllerName,
string areaName, string cacheKey, ref string[] searchedLocations)
{
string virtualPath = string.Empty;
searchedLocations = new string[locations.Length];
for (int i = 0; i < locations.Length; i++)
{
if (string.IsNullOrEmpty(areaName) && locations[i].Contains("{2}"))
{
continue;
}
string testPath = string.Format(CultureInfo.InvariantCulture,
locations[i], name, controllerName, areaName);
if (FileExists(controllerContext, testPath))
{
searchedLocations = EmptyLocations;
virtualPath = testPath;
ViewLocationCache.InsertViewLocation(
controllerContext.HttpContext, cacheKey, virtualPath);
return virtualPath;
}
searchedLocations[i] = testPath;
}
return virtualPath;
}
protected string GetPathFromSpecificName(
ControllerContext controllerContext, string name, string cacheKey,
ref string[] searchedLocations)
{
string virtualPath = name;
if (!FileExists(controllerContext, name))
{
virtualPath = string.Empty;
searchedLocations = new string[] { name };
}
ViewLocationCache.InsertViewLocation(controllerContext.HttpContext,
cacheKey, virtualPath);
return virtualPath;
}
protected string getArea(ControllerContext controllerContext)
{
// First try to get area from a RouteValue override, like one specified in the Defaults arg to a Route.
object areaO;
controllerContext.RouteData.Values.TryGetValue("area", out areaO);
// If not specified, try to get it from the Controller's namespace
if (areaO != null)
return (string)areaO;
string namespa = controllerContext.Controller.GetType().Namespace;
int areaStart = namespa.IndexOf("Areas.");
if (areaStart == -1)
return null;
areaStart += 6;
int areaEnd = namespa.IndexOf('.', areaStart + 1);
string area = namespa.Substring(areaStart, areaEnd - areaStart);
return area;
}
protected static bool IsSpecificPath(string name)
{
char ch = name[0];
if (ch != '~')
{
return (ch == '/');
}
return true;
}
}
さて、前述の通り、これは具体的なエンジンではないので、それも作らなければなりません。 この部分は、幸いなことに 大いに デフォルトのフォーマットを設定し、実際にビューを作成する必要があるだけです。
AreaAwareViewEngine.cs
public class AreaAwareViewEngine : BaseAreaAwareViewEngine
{
public AreaAwareViewEngine()
{
MasterLocationFormats = new string[]
{
"~/Areas/{2}/Views/{1}/{0}.master",
"~/Areas/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Views/Shared/{0}.master",
"~/Areas/{2}/Views/Shared/{0}.cshtml",
"~/Views/{1}/{0}.master",
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.master"
"~/Views/Shared/{0}.cshtml"
};
ViewLocationFormats = new string[]
{
"~/Areas/{2}/Views/{1}/{0}.aspx",
"~/Areas/{2}/Views/{1}/{0}.ascx",
"~/Areas/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Views/Shared/{0}.aspx",
"~/Areas/{2}/Views/Shared/{0}.ascx",
"~/Areas/{2}/Views/Shared/{0}.cshtml",
"~/Views/{1}/{0}.aspx",
"~/Views/{1}/{0}.ascx",
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.aspx"
"~/Views/Shared/{0}.ascx"
"~/Views/Shared/{0}.cshtml"
};
PartialViewLocationFormats = ViewLocationFormats;
}
protected override IView CreatePartialView(
ControllerContext controllerContext, string partialPath)
{
if (partialPath.EndsWith(".cshtml"))
return new System.Web.Mvc.RazorView(controllerContext, partialPath, null, false, null);
else
return new WebFormView(controllerContext, partialPath);
}
protected override IView CreateView(ControllerContext controllerContext,
string viewPath, string masterPath)
{
if (viewPath.EndsWith(".cshtml"))
return new RazorView(controllerContext, viewPath, masterPath, false, null);
else
return new WebFormView(controllerContext, viewPath, masterPath);
}
}
標準の
ViewLocationFormats
. これらは、新しい
{2}
のエントリで、ここで
{2}
にマップされます。
area
の中に置いた
RouteData
. を残したまま
MasterLocationFormats
を残していますが、もちろん変更することも可能です。
では、あなたの
global.asax
を修正して、このビューエンジンを登録します。
グローバル.asax.cs
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new AreaAwareViewEngine());
}
...そして、デフォルトルートを登録します。
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Area",
"",
new { area = "AreaZ", controller = "Default", action = "ActionY" }
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
}
ここで
AreaController
を作成します。
DefaultController.cs (~/Controllers/ にあります)
public class DefaultController : Controller
{
public ActionResult ActionY()
{
return View("TestView");
}
}
もちろん、ディレクトリ構造とそれに付随するビューが必要ですが、ここでは超シンプルにします。
TestView.aspx (~/Areas/AreaZ/Views/Default/ または ~/Areas/AreaZ/Views/Shared/ にある)
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<h2>TestView</h2>
This is a test view in AreaZ.
で、終わりです。 ついに完成 .
たいていの場合、あなたは単に
BaseAreaAwareViewEngine
と
AreaAwareViewEngine
を追加して、任意のMVCプロジェクトにドロップするだけなので、これを実現するために多くのコードが必要だったとしても、一度だけ書けばいいのです。 それ以降は
global.asax.cs
の数行を編集し、サイト構造を作成するだけです。
関連
-
[解決済み] glyphicons-halflings-regular.woff2 not foundに関するエラーを削除する方法
-
[解決済み] データベースに 'AspNetRoles' という名前のオブジェクトがすでに存在します。
-
[解決済み] コントローラ '...' でパブリックアクションメソッド '...' が見つかりませんでした。
-
[解決済み] ファイルアップロード ASP.NET MVC 3.0
-
[解決済み] MVCで、文字列の結果を返すにはどうしたらいいですか?
-
[解決済み] ASP.NET MVC - カスタムIIdentityまたはIPrincipalの設定
-
[解決済み】ASP.NET MVCで別のコントローラからビューを表示する
-
[解決済み] [Solved] ASP.NET MVCで404を適切に処理するには?
-
[解決済み] asp.net mvc: なぜ Html.CheckBox は追加の隠された入力を生成するのですか?
-
[解決済み】AutoMapper.CreateMapsをどこに配置するか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Visual Studioのデバッグ/ロードが非常に遅い
-
[解決済み] MVC 3 - 辞書に渡されたモデル項目の型は 'System.Collections.Generic.List`1 です。
-
[解決済み] Automapper のタイプマップ構成がない、またはサポートされていないマッピング - エラー
-
[解決済み】Html5 data-* with asp.net mvc TextboxFor html attributes
-
[解決済み】ASP.NET MVC Html.ValidationSummary(true) でモデルエラーが表示されない。
-
[解決済み】Java Server Faces 2.0の主なデメリットは何ですか?
-
[解決済み] ASP.NET MVCのモデルでUrlHelperを呼び出す
-
[解決済み] アクションリンクでエリア名を指定するには?
-
[解決済み] ソートロジックは、モデル、ビュー、コントローラのいずれに配置するのがよいのでしょうか?[クローズド]
-
[解決済み] NerdDinnerでASP.NET MVCでModelState.IsValidは何のために有効なのか?