これが私が金曜日から髪を引き裂いてきた問題です.
単一のクライアントに対して複数の異なるサブグループを提供する単一の MVC アプリケーションがあります。ブランディングおよび一部のスタイル要素では、URL を次のようにフォーマットする必要があります。 www.site.com/Login www.site.com/Client1/Login www.site.com/Client2/Login www.site.com/Client3/Login等々。
また、この構造を維持し、www.site.com/Client1/News などに移行したいと考えています。
スタティック ルーティングは検討の余地がありません。それらを生成するツールでさえ。このサイトには、Y クライアント向けの独自のルートを持つ X ページがあり、そのパフォーマンスについて考えるとゾッとします。そして、動的な性質のため、その場で仮想ディレクトリを作成することは、私が下に行きたいルートではありません.
最初は、解決策は些細なことに思えました。2 つのテスト ソリューションを試しました。
最初は CustomRouteBase から派生し、オーバーライドされた GetRouteData メソッドで正しいルートを特定し、GetVirtualPath を使用して正しい Url を生成することができました。2 番目のソリューションでは、制約を使用して、クライアントがパターン内にあるかどうかを確認し、それに応じてルーティングしました。どちらも正しいコントローラーをヒットし、正しいリンクを生成します。
次に、エリアを追加しました (これは単なるプロトタイプですが、実際のサイトではエリアを使用しています)。
どちらのソリューションも失敗しました。領域は適切に登録され、通常どおりに機能しました。しかし、最初の解決策では、領域登録のために GetVirtualPath をオーバーライドする方法が見つかりませんでした。Area クラスから拡張メソッドがあることは知っていますが、これは私が必要としているものに適合しません。
制約も使用してみましたが、URL の「クライアント」部分がエリアへのアクション リンクのいずれにも追加されず、制約を使用してコントローラーにルーティングしようとすると、古き良きビューが見つからないというエラーが発生しました (検索ルートでエリアを指定したにもかかわらず、ルートから)。
だから私の最初の質問は、これについて間違った方法をとっているのでしょうか? これを達成するためのより良い方法があれば、私はすべての時代です. そうでない場合、どのようにエリアを管理しますか?
ここにいくつかのコードを入れることもできますが、それはすべて、私がやりたいことのために機能します。エリアの問題にどうアプローチするか迷っています。しかし、残念なことに、いつものように私はこのプロジェクトをローンチの 3 か月前に継承しました。私のチームには、それらなしでサイトを再構築するためのリソースがありません。
@Max ...似たようなことを試しましたが、www.site.com/Client1の場合、領域はまだリンクを正しく表示しません...これはプロトタイプ6のものです。いくつかの異なる方法を試しました。
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//throws view engine can't find view error. If I use www.website.com/Client1, hvering over area links does not give the correct path
routes.MapRoute("Area",
"{folder}/{area}/{controller}/{action}/{id}",
new { area = "Authentication", controller = "Authentication", action = "Index", id = UrlParameter.Optional },
new { folder = new IsFolderContsraint(), area = new IsArea() }
);
//works fine.. if I use www.website.com/Client1, hovering over regular (non area) links works fine
routes.MapRoute("Branded",
"{folder}/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { folder = new IsFolderContsraint() }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
これが私がそれに対処しようとした別の方法です。実装をクラックしないでください。これは単なる POC です。ここでの問題は、Area が RouteBase の一部ではないため、Area の仮想パスを変更できないことです。したがって、すべてのアクション リンクなどは正しくレンダリングされ、アクションが領域内にない限り、すべて正常に機能します。
public class Folders
{
private static Dictionary<string, string> _folders= new Dictionary<string, string>()
{ {"test1", "style1"},
{"test2", "style2"},
{"test3", "style3"}
};
public static Dictionary<string, string> FolderNames { get { return _folders; } }
}
public class AreaDefinitions
{
private static Dictionary<string, string> _areas = new Dictionary<string, string>()
{ {"Authentication", "Authentication"} };
public static Dictionary<string, string> AreaDefinition { get { return _areas; } }
}
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add(new CustomRouteBase());
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}
public class CustomRouteBase : RouteBase
{
public override RouteData GetRouteData(HttpContextBase httpContext)
{
//Creates routes based on current app execution pass
//a bit like doing my own routing constraints, but is more dynamic.
//get route data (and CreateVirtualPath) will be called for any action needing to be rendered in the current view.
//But not for areas :( Ugly but just a prototype
string url = httpContext.Request.AppRelativeCurrentExecutionFilePath;
url = url.StartsWith("~/") ? url.Substring(2, url.Length - 2) : url;
string[] urlParts = url.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
RouteData rd = new RouteData(this, new MvcRouteHandler());
if (urlParts.Length == 0)
{
rd.Values.Add("controller", urlParts.Length > 0 ? urlParts[0] : "Home");
rd.Values.Add("action", urlParts.Length > 1 ? urlParts[1] : "Index");
return rd;
}
if (Folders.FolderNames.ContainsKey(urlParts[0]))
{
if (urlParts.Length > 1 && AreaDefinitions.AreaDefinition.ContainsKey(urlParts[1]))
{
rd.DataTokens["area"] = urlParts[1];
rd.Values.Add("controller", urlParts.Length > 2 ? urlParts[2] : "Home");
rd.Values.Add("action", urlParts.Length > 3 ? urlParts[3] : "Index");
}
else
{
rd.Values.Add("controller", urlParts.Length > 1 ? urlParts[1] : "Home");
rd.Values.Add("action", urlParts.Length > 2 ? urlParts[2] : "Index");
}
rd.DataTokens.Add("folder", urlParts[0]);
}
else
{
rd.Values.Add("controller", urlParts.Length > 0 ? urlParts[0] : "Home");
rd.Values.Add("action", urlParts.Length > 1 ? urlParts[1] : "Index");
}
return rd;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
//Assembled virtial path here using route and values.
//Worked (ugly but functioned, but was never called for areas.)
string url = "";
if (requestContext.RouteData.DataTokens.ContainsKey("folder"))
url += requestContext.RouteData.DataTokens["folder"] + "/";
if (values.ContainsKey("controller"))
url += values["controller"] + "/";
if (values.ContainsKey("action"))
url += values["action"];
var vpd = new VirtualPathData(requestContext.RouteData.Route, url);
return vpd;
}
}
ありがとう、リアム...これは、ビューのオーバーライドのために最初に「プラグイン」フォルダーから読み取るように、ビューエンジンをカスタマイズする方法に似ています。しかし、問題はここで少し異なります。これは、ビュー オーバーライドを備えたサイトを既に持っている 1 つのクライアントですが、独自の複数のクライアントを持っています。ここでの動作は、スタイル シートやロゴなどを制御するためだけのものです。ユーザーがログインした後、スタイルとブランディングがどうあるべきかを特定できますが、クライアントはランディング ページに "www. site.com/Client1" を使用してこれを識別します。私はあきらめて、リクエストを www.site.com/?client=client1 に変換するだけのハンドラーを作成して、ランディング ページのスタイルを処理できるようにしましたが、URL を www.ste.com のままにしておく方がずっといいでしょう。 /Client1/ログインなど これは、以前は vdir を使用してさまざまなディレクトリをホストしていた従来の ASP アプリからの変換です。潜在的なクライアントの数 (100 の) のため、静的ルーティングは重くなります。私の解決策はすべてある程度機能します...すべての問題を引き起こしているのはその領域です。RouteBase で作成したルートでできるように、それらの仮想パスを動的に再マッピングする方法を見つけることができれば、私は仕事をしていると思います.