はい、これは以前に尋ねられましたが、そこに固い解決策はありません。それで、私自身と他の人の目的のために、これは役に立つと思うかもしれません。
MVC2(ASP.NET)では、誰かがWebサイトに移動したときにデフォルトの領域が指定されるようにしたいのです。したがって、私のサイトに移動すると、AreaZのControllerX ActionYに移動します。
Global.asaxで次のルートを使用する
routes.MapRoute(
"Area",
"",
new { area = "AreaZ", controller = "ControllerX ", action = "ActionY " }
);
これは、正しいページを提供しようとするのと同じように機能します。ただし、MVCは、Areaフォルダーではなく、サイトのルートでビューを探します。
これを解決する方法はありますか?
編集
「ソリューション」があり、それはControllerXにあり、ActionYはビューのフルパスを返します。ハッキングのビットが、それは動作します。しかし、私はより良い解決策があることを望んでいます。
public ActionResult ActionY()
{
return View("~/Areas/AreaZ/views/ActionY.aspx");
}
編集:
これは、ページのHTML ActionLinkがある場合にも問題になります。エリアが設定されていない場合、アクションリンクは空白で出力されます。
これはすべて設計によるものですか、それとも欠陥ですか?
これは私に興味があり、ついにそれを調べる機会がありました。他の人は、これがviewを見つけるの問題であることを理解していないようです。routing自体の問題ではありません-それはおそらく、質問のタイトルがルーティングに関するものであることを示しているためです。
いずれにしても、これはビュー関連の問題であるため、必要なものを取得する唯一の方法はデフォルトのビューエンジンをオーバーライドするです。通常、これを行うときは、ビューエンジンを切り替えるという単純な目的のためです(つまり、Spark、NHamlなどに)。この場合、これはオーバーライドする必要があるビュー作成ロジックではなく、FindPartialView
クラスのFindView
およびVirtualPathProviderViewEngine
メソッドです。
VirtualPathProviderViewEngine
の他のすべてが偶数accessibleではないため、これらのメソッドが実際に仮想であることをラッキースターに感謝できます。そして、それは非常に非常に迷惑になります。それは、基本的に、ニースをプレイしたい場合、すでに書かれたコードの半分を書き直さなければならないので、検索ロジックをオーバーライドしますロケーションキャッシュとロケーション形式を使用します。 Reflectorを掘り下げた後、ようやく実用的なソリューションを思いついた。
ここで行ったことは、最初にAreaAwareViewEngine
ではなくVirtualPathProviderViewEngine
から直接派生する抽象WebFormViewEngine
を作成することです。代わりにSparkビュー(または何でも)を作成する場合、このクラスを基本型として使用できるように、これを行いました。
以下のコードはかなり長いので、実際に何をするのかを簡単にまとめてみましょう:{2}
をコントローラー名に対応するのと同じように、エリア名に対応する{1}
をロケーション形式に入れることができます。それでおしまい!これは、次のすべてのコードを記述する必要がありました。
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;
}
}
今述べたように、これは具体的なエンジンではないため、同様に作成する必要があります。幸いなことに、この部分はmuchより簡単で、デフォルトのフォーマットを設定し、実際にビューを作成するだけです。
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
を変更して、このビューエンジンを登録します。
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
を作成します。
public class DefaultController : Controller
{
public ActionResult ActionY()
{
return View("TestView");
}
}
当然、それに合わせてディレクトリ構造とビューが必要です。これを非常にシンプルに保ちます。
<%@ 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
の数行を編集して、サイト構造を作成するだけです。
これが私がやった方法です。 MapRoute()がエリアの設定を許可しない理由はわかりませんが、ルートオブジェクトが返されるので、必要に応じて追加の変更を続けることができます。私がこれを使用するのは、企業の顧客に販売されているモジュラーMVCサイトがあり、新しいモジュールを追加するためにbinフォルダーにdllをドロップできる必要があるためです。 AppSettings構成で「HomeArea」を変更できるようにします。
var route = routes.MapRoute(
"Home_Default",
"",
new {controller = "Home", action = "index" },
new[] { "IPC.Web.Core.Controllers" }
);
route.DataTokens["area"] = area;
編集:デフォルトでユーザーが行きたいエリアのAreaRegistration.RegisterAreaでもこれを試すことができます。テストしていませんが、AreaRegistrationContext.MapRouteはroute.DataTokens["area"] = this.AreaName;
を設定します。
context.MapRoute(
"Home_Default",
"",
new {controller = "Home", action = "index" },
new[] { "IPC.Web.Core.Controllers" }
);
すでに回答済みです-これは短い構文です(ASP.net 3、4、5):
routes.MapRoute("redirect all other requests", "{*url}",
new {
controller = "UnderConstruction",
action = "Index"
}).DataTokens = new RouteValueDictionary(new { area = "Shop" });
ビューの位置を特定することだと指摘してくれたアーロンに感謝します。
[更新]コードまたはルックアップパスをいじらずに、デフォルトでユーザーをエリアに送信するプロジェクトを作成しました。
Global.asaxで、通常どおりに登録します。
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = ""} // Parameter defaults,
);
}
Application_Start()
では、必ず次の順序を使用してください。
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
}
あなたの地域での登録、使用
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"ShopArea_default",
"{controller}/{action}/{id}",
new { action = "Index", id = "", controller = "MyRoute" },
new { controller = "MyRoute" }
);
}
例は http://www.emphess.net/2010/01/31/areas-routes-and-defaults-in-mvc-2-rc/ にあります。
これがあなたが求めていたものであることを本当に願っています...
////
この場合、擬似ViewEngine
を書くことが最善の解決策だとは思いません。 (評判がなく、コメントできません)。 WebFormsViewEngine
はエリア対応であり、デフォルトで次のように定義されるAreaViewLocationFormats
を含みます。
AreaViewLocationFormats = new[] {
"~/Areas/{2}/Views/{1}/{0}.aspx",
"~/Areas/{2}/Views/{1}/{0}.ascx",
"~/Areas/{2}/Views/Shared/{0}.aspx",
"~/Areas/{2}/Views/Shared/{0}.ascx",
};
私はあなたがこの慣習に従わないと信じています。投稿しました
public ActionResult ActionY()
{
return View("~/Areas/AreaZ/views/ActionY.aspx");
}
働くハックとして、しかしそれはあるべきです
return View("~/Areas/AreaZ/views/ControllerX/ActionY.aspx");
ただし、規則に従わない場合は、WebFormViewEngine
(たとえば、MvcContribで行われます)から派生することにより、短いパスを使用することができます。コンストラクタ、またはApplication_Start
で次のような規則を指定することで-少しハックします:
((VirtualPathProviderViewEngine)ViewEngines.Engines[0]).AreaViewLocationFormats = ...;
もちろん、これはもう少し注意して実行する必要がありますが、アイデアを示していると思います。これらのフィールドは、MVC 2 RCのpublic
のVirtualPathProviderViewEngine
です。
ユーザーが~/AreaZ
URLに一度アクセスすると、~/
URLにリダイレクトされるようにしたいと思います。ルートHomeController
内で次のコードを使用して達成します。
public class HomeController
{
public ActionResult Index()
{
return RedirectToAction("ActionY", "ControllerX", new { Area = "AreaZ" });
}
}
Global.asax
の次のルート。
routes.MapRoute(
"Redirection to AreaZ",
String.Empty,
new { controller = "Home ", action = "Index" }
);
Application_Startに次を追加するとうまくいきますが、RCにこの設定があるかどうかはわかりません。
var engine = (WebFormViewEngine)ViewEngines.Engines.First();
// These additions allow me to route default requests for "/" to the home area
engine.ViewLocationFormats = new string[] {
"~/Views/{1}/{0}.aspx",
"~/Views/{1}/{0}.ascx",
"~/Areas/{1}/Views/{1}/{0}.aspx", // new
"~/Areas/{1}/Views/{1}/{0}.ascx", // new
"~/Areas/{1}/Views/{0}.aspx", // new
"~/Areas/{1}/Views/{0}.ascx", // new
"~/Views/{1}/{0}.ascx",
"~/Views/Shared/{0}.aspx",
"~/Views/Shared/{0}.ascx"
};
まず、使用しているMVC2のバージョンは何ですか? preview2からRCに大きな変更がありました。
RCを使用すると仮定すると、ルートマッピングの外観は異なるはずです。お住まいの地域のAreaRegistration.cs
では、何らかのデフォルトルートを登録できます。
context.MapRoute(
"ShopArea_default",
"{controller}/{action}/{id}",
new { action = "Index", id = "", controller="MyRoute" }
);
上記のコードは、デフォルトでMyRouteController
のShopArea
にユーザーを送信します。
2番目のパラメーターとして空の文字列を使用すると、コントローラーを指定する必要があるため、例外がスローされます。
もちろん、Global.asax
のデフォルトルートを変更する必要があるため、このデフォルトルートに干渉しません。メインサイトのプレフィックスを使用します。
このスレッドとHaackの回答も参照してください。 MVC 2 AreaRegistration Routes Order
お役に立てれば。
これを機能させるために私がしたことは次のとおりです。
コントローラーに次のコードを追加しました。
namespace MyNameSpace.Controllers {
public class DefaultController : Controller {
// GET: Default
public ActionResult Index() {
return RedirectToAction("Index", "ControllerName", new {area = "FolderName"});
}
} }
RouterConfig.csに次を追加しました。
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new {controller = "Default", action = "Index", id = UrlParameter.Optional});
これらすべての背後にある秘appは、アプリを起動するたびに常にスタートアップコントローラーになる既定のコンストラクターを作成したことです。デフォルトのコントローラーにヒットすると、デフォルトのインデックスアクションで指定したコントローラーにリダイレクトされます。私の場合は
www.myurl.com/FolderName/ControllerName
。
さまざまな構成要素の特定は、リクエストのライフサイクルで行われます。 ASP.NET MVCリクエストライフサイクルの最初のステップの1つは、リクエストされたURLを正しいコントローラーアクションメソッドにマッピングすることです。このプロセスはルーティングと呼ばれます。デフォルトルートはGlobal.asaxファイルで初期化され、ASP.NET MVCフレームワークにリクエストの処理方法を説明します。 MvcApplication1プロジェクトのGlobal.asaxファイルをダブルクリックすると、次のコードが表示されます。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing;
namespace MvcApplication1 {
public class GlobalApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index",
id = "" } // Parameter defaults
);
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
}
}
}
アプリケーションがコンパイルされるかWebサーバーが再起動されるたびに起動されるApplication_Start()イベントハンドラーで、ルートテーブルが登録されます。デフォルトルートはDefaultという名前で、 http://www.example.com/ {controller}/{action}/{id}の形式でURLに応答します。 {と}の間の変数には、リクエストURLの実際の値、またはURLにオーバーライドが存在しない場合はデフォルト値が入力されます。このデフォルトルートは、デフォルトのルーティングパラメーターに従って、HomeコントローラーとIndexアクションメソッドにマッピングされます。このルーティングマップでは、他のアクションはありません。
デフォルトでは、すべての可能なURLはこのデフォルトルートを介してマッピングできます。独自のルートを作成することもできます。たとえば、URL http://www.example.com/Employee/Maarten をEmployeeコントローラー、Showアクション、およびfirstnameパラメーターにマッピングしましょう。次のコードスニペットは、開いたばかりのGlobal.asaxファイルに挿入できます。 ASP.NET MVCフレームワークは最初に一致するルートを使用するため、このコードスニペットはデフォルトルートの上に挿入する必要があります。それ以外の場合、ルートは使用されません。
routes.MapRoute(
"EmployeeShow", // Route name
"Employee/{firstname}", // URL with parameters
new { // Parameter defaults
controller = "Employee",
action = "Show",
firstname = ""
}
);
次に、このルートに必要なコンポーネントを追加しましょう。まず、ControllersフォルダーにEmployeeControllerという名前のクラスを作成します。これを行うには、プロジェクトに新しいアイテムを追加し、Web |の下にあるMVC Controller Classテンプレートを選択します。 MVCカテゴリー。 Indexアクションメソッドを削除し、Showという名前のメソッドまたはアクションに置き換えます。このメソッドは、firstnameパラメーターを受け入れ、データをViewDataディクショナリーに渡します。この辞書は、データを表示するためにビューによって使用されます。
EmployeeControllerクラスは、ビューにEmployeeオブジェクトを渡します。このEmployeeクラスをModelsフォルダーに追加する必要があります(このフォルダーを右クリックして、コンテキストメニューから[追加] | [クラス]を選択します)。 Employeeクラスのコードは次のとおりです。
namespace MvcApplication1.Models {
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
}
この質問の受け入れられた解決策は、カスタムビューエンジンの作成方法を要約すると正しいですが、質問に正しく答えられません。ここでの問題は、Pinoがデフォルトルートを誤って指定しているです。特に彼の「エリア」の定義は間違っています。 「エリア」はDataTokensコレクションを介してチェックされ、次のように追加する必要があります。
var defaultRoute = new Route("",new RouteValueDictionary(){{"controller","Default"},{"action","Index"}},null/*constraints*/,new RouteValueDictionary(){{"area","Admin"}},new MvcRouteHandler());
defaultRoute.DataTokens.Add("Namespaces","MyProject.Web.Admin.Controller");
routes.Add(defaultRoute);
デフォルトオブジェクトで指定された「エリア」は無視されます。上記のコードはデフォルトルートを作成します。デフォルトルートは、サイトのルートへのリクエストをキャッチし、管理エリアでデフォルトコントローラ、インデックスアクションを呼び出します。 「名前空間」キーがDataTokenに追加されることにも注意してください。これは、同じ名前のコントローラーが複数ある場合にのみ必要です。このソリューションは、Mvc2およびMvc3 .NET 3.5/4.0で検証されています
さて、カスタムビューエンジンを作成することでこれを実現できますが、それでも別の方法を使用できます。
public override void RegisterArea(AreaRegistrationContext context) { //this makes it work for the empty url (just domain) to act as current Area. context.MapRoute( "Area_empty", "", new { controller = "Home", action = "Index", id = UrlParameter.Optional }, namespaces: new string[] { "Area controller namespace" } ); //other routes of the area }
乾杯!
routes.MapRoute(
"Area",
"{area}/",
new { area = "AreaZ", controller = "ControlerX ", action = "ActionY " }
);
試しましたか?