MVCで(エリアへの)デフォルトルートを設定する方法


122

さて、これは以前に尋ねられましたが、そこには固い解決策はありません。だから私やこれが便利だと思う人のために。

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がある場合にも問題になります。エリアが設定されていない場合、アクションリンクは空白で出力されます。

これはすべて設計上の欠陥ですか、それとも欠陥ですか?

回答:


98

これは私に興味があり、ついにそれを調べる機会がありました。他の人々は、これがルーティングの問題ではなく、ビューの検索の問題であることを理解していないようです。これはおそらく、質問のタイトルがルーティングに関するものであることを示しているためです。

いずれにしても、これはビュー関連の問題であるため、必要なものを取得する唯一の方法は、デフォルトのビューエンジンオーバーライドすることです。通常、これを行うときは、ビューエンジンを切り替える(Spark、NHamlなどに切り替える)ための単純な目的です。この場合、オーバーライドする必要のあるビュー作成ロジックではなく、クラスのFindPartialViewおよびFindViewメソッドVirtualPathProviderViewEngineです。

これらのメソッドは実際には仮想であるというラッキースターに感謝することができます。これは、他のすべてのメソッドにはアクセスできVirtualPathProviderViewEngineないため、プライベートであり、基本的にはすでにあるコードの半分を書き直す必要があるため、検索ロジックをオーバーライドするのが非常に面倒です。場所のキャッシュと場所の形式をうまく利用したい場合に書かれています。リフレクターを少し掘り下げた後、私はようやく実用的なソリューションを思いつくことができました。

ここで行ったことは、最初に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、このビューエンジンを登録します。

Global.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.

以上です。 最後に、これで完了です。

ほとんどの場合、BaseAreaAwareViewEngineand AreaAwareViewEngineを取得して任意のMVCプロジェクトにドロップできるはずです。そのため、これを実行するのに多くのコードを必要としましたが、一度作成するだけで済みます。その後は、数行を編集しglobal.asax.csてサイト構造を作成するだけです。


これは最も好意的に現在の最良のソリューションですが、理想からはほど遠いです。上記のように、アクションリンクを追加すると、同じ問題が発生します。
LiamB 2010

1
@Pino:で「デフォルト」のルートマッピングにActionLink同じものarea = "AreaZ"を追加することで問題を解決できるはずglobal.asax.csです。私は前向きではありません。試してみてください。
アーロンノート、2010

MVC4の「デフォルト」ルート宣言がGlobal.asaxから〜/ App_Start / RouteConfig.cs / RegisterRoutes()に移動
Andriy F.

3
私は反対票を投じるのが嫌いですが、@ Chris Aldersonによる以下の回答がこれ以上票を獲得していないとは本当に信じられません。これはこれよりもはるかに単純なソリューションであり、エッジケース(ActionLinksなど)を解決するようです。
jdmcnair 2014年

ここにバグがあるようです。たとえば、「Re」という名前のエリアのビューは〜/ Areas / Re / Views / Ctrlr / blah.aspxにありますが、ここのコードは〜/ {2} / {1} / {0}を使用します。 /Re/Ctrl/blah.aspx、パスに重要なAreasディレクトリがありません。"〜/ Areas / {2} / Views / {1} / {0} .aspx"
Chris Moschini 2015

100

これは私がやった方法です。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" }
                   );

できます。新しいweb.configファイルに注意してください。古いグローバル構成が上書きされる可能性があります。
Mert Akcakaya 2014

56

それはすでに答えられていても-これは短い構文です(ASP.net 3、4、5):

routes.MapRoute("redirect all other requests", "{*url}",
    new {
        controller = "UnderConstruction",
        action = "Index"
        }).DataTokens = new RouteValueDictionary(new { area = "Shop" });

6
これは私にとってはうまくいきます。ルートにはコントローラーがなく、エリアのみを使用しています。MVC 4の場合、これをRouteConfig.csのデフォルトに置き換えます。ありがとう!
Marc

2
私はMVC4を使用していますが、これが最も簡単な解決策でした。特定のエリア内のインデックスビューをサイトの「ホームページ」として使用することをアプリケーションに許可します。
JTech 2013年

2
このソリューションは将来は機能しません(Asp.Net MVC6以降)。
Patrick Desjardins、2015年

@PatrickDesjardins:上記のソリューションをサポートしない理由はありますか?
Akash KC 2016

@SeriousMあなたの答えは常緑樹です。それはまだ役に立ちます。あなたは私を一夜救った。
skpaul

16

ビューを見つけることだと指摘してくれたアーロンに感謝しますが、私はそれを誤解しました。

[更新]私は、コードやルックアップパスをいじらずに、デフォルトでユーザーをエリアに送信するプロジェクトを作成しました。

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で行われる)から派生するか、または-a少しハッキー-このようにあなたの慣習を指定することによってApplication_Start

((VirtualPathProviderViewEngine)ViewEngines.Engines[0]).AreaViewLocationFormats = ...;

もちろん、これはもう少し注意して実行する必要がありますが、アイデアを示していると思います。これらのフィールドは、publicVirtualPathProviderViewEngineMVC 2 RCに。


これはMVC 2 RCにのみ適用されることに注意してください。MVC1にVirtualPathProviderViewEngineはこのプロパティがなく、エリアを認識しません。そして、この質問は確かにMVC 2についてであると述べられていましたが、多くの人々はまだそれを使用していません(そしてしばらくの間は使用しません)。だから、あなたの答えは特定の質問の方が簡単ですが、私がこの質問に出くわしたMVC1ユーザーのために働く唯一のものです。変更される可能性のあるプレリリース機能に依存しない回答を提供したいと思います。
アーロノート

また、これは「疑似ビューエンジン」ではありません。ビューエンジンクラスは、さまざまな種類のビューを使用できるように意図的に拡張可能に作成されています。
アーロンノート

それはあなたを侮辱するためのものではありませんでした、申し訳ありません。ビューの処理方法を大幅に変更するのではなく、一部の値を置き換えるだけであるという点で、これは「疑似」です。
mnemosyn 2010年

私は気分を害したのではなく、関連するメソッドがオーバーライド可能であるという事実によって証明されているように、カスタムビューエンジンを派生することは特に珍しい理由ではないという事実を明らかにしたかっただけです。
アーロノート

2
RegisterAreas前に行くことについての大きなヒントRegisterRoutes。なぜ私のコードが突然動作を停止し、そのリファクタリングに気付いたのか疑問に思っていました;)
webnoob

6

ユーザーが~/AreaZURLにアクセスした後に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" }
);

これは機能しますが、ユーザーのブラウザーでURLに変わります。本当に理想的ではありません。
LiamB 2010年

2

まず、使用しているMVC2のバージョンは何ですか?preview2からRCに大幅な変更が加えられました。

RCを使用していると想定すると、ルートマッピングは異なるように見えるはずです。AreaRegistration.csお住まいの地域で、あなたは例えば、デフォルトルートのいくつかの種類を登録することができます

        context.MapRoute(
            "ShopArea_default",
            "{controller}/{action}/{id}",
            new { action = "Index", id = "", controller="MyRoute" }
        );

上記のコードはMyRouteControllerShopAreaデフォルトでユーザーをに送ります。

コントローラーを指定する必要があるため、2番目のパラメーターとして空の文字列を使用すると、例外がスローされます。

もちろんGlobal.asax、メインサイトのプレフィックスを使用するなどして、このデフォルトルートに干渉しないようにデフォルトルートを変更する必要があります。

このスレッドとHaackの回答もご覧ください:MVC 2 AreaRegistration Routes Order

お役に立てれば。


ありがとうございます。ただし、質問で説明されている問題がこれで解決するかどうかはわかりません。そしてそのMVC RC
LiamB 2010年

2

次のコードを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"
};

1

これを機能させるために私がしたことは次のとおりです:

  1. root / Controllersフォルダーにデフォルトのコントローラーを作成しました。コントローラにDefaultControllerという名前を付けました。
  2. コントローラに次のコードを追加しました:

    namespace MyNameSpace.Controllers {
    public class DefaultController : Controller {
        // GET: Default
        public ActionResult Index() {
            return RedirectToAction("Index", "ControllerName", new {area = "FolderName"});
        }
    } }
  3. 私のRouterConfig.csに以下を追加しました:

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new {controller = "Default", action = "Index", id = UrlParameter.Optional});

これらすべての背後にある秘訣は、アプリを起動するたびに常に起動コントローラーになるデフォルトのコンストラクターを作成したことです。そのデフォルトのコントローラーに到達すると、デフォルトのインデックスアクションで指定したコントローラーにリダイレクトされます。私の場合はどちらですか

www.myurl.com/FolderName/ControllerName


0
routes.MapRoute(
                "Area",
                "{area}/",
                new { area = "AreaZ", controller = "ControlerX ", action = "ActionY " }
            );

試しましたか?


はい、問題はサイトがルートでビューを探すという事実にあります。ビュー 'ActionY'またはそのマスターが見つかりませんでした。次の場所が検索されました:〜/ Views / ActionY / ActionY.aspx〜/ Views / ActionY / ActionY.ascx〜/ Views / Shared / ActionY.aspx〜/ Views / Shared / ActionY.ascx
LiamB

2
わかります。私は解決策を見つけようとします。質問の+1
Barbaros Alp

0

さまざまなビルディングブロックの検索は、リクエストのライフサイクルで行われます。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/の形式のURLに応答します {controller} / {action} / {id}。{と}の間の変数には、リクエストURLからの実際の値、またはURLにオーバーライドが存在しない場合はデフォルト値が入力されます。このデフォルトルートは、デフォルトのルーティングパラメータに従って、HomeコントローラとIndexアクションメソッドにマッピングされます。このルーティングマップでは他のアクションはありません。

デフォルトでは、すべての可能なURLをこのデフォルトルートを介してマップできます。独自のルートを作成することも可能です。たとえば、http://www.example.com/Employee/MaartenというURL を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; }
   }

} 

1
おかげで、これがデフォルトのAREAの設定にどのように関係するのかはよくわかりません。:-/
LiamB 2010年

0

ええと、カスタムビューエンジンを作成することでこれを行うことができますが、別の方法もあります。

  • デフォルトで表示する必要があるものを決定します。
  • その何かにはコントローラーとアクション(そしてエリア)がありますよね?
  • そのArea登録を開き、次のようなものを追加します。
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
}

乾杯!


同意した。このルート定義のより適切な場所はGlobal.asaxファイルにあると思いますが。
nuhusky2003 2012

そのような場合、global.asaxの定義は、エリアコントローラーの名前空間の存在を認識しますが、これは正しくないと思います。エリアは追加された機能です。つまり、global.asax定義に触れずにエリアを追加/削除できる必要があります。質問に対する私のアプローチでは、リクエストを「引き継ぐ」ための[グローバル] Webサイトではなく、リクエストを「引き継ぐ」ための領域を好みます。
Tengiz

0

この質問に対する受け入れられた解決策は、カスタムビューエンジンの作成方法を要約すると正しいですが、質問に正しく回答しません。ここでの問題は、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);

デフォルトオブジェクトで指定された「エリア」は無視されます。上記のコードは、デフォルトルートを作成します。これは、サイトのルートへのリクエストをキャッチし、デフォルトコントローラー、管理領域のインデックスアクションを呼び出します。「名前空間」キーがDataTokensに追加されていることにも注意してください。これは、同じ名前のコントローラが複数ある場合にのみ必要です。このソリューションはMvc2およびMvc3 .NET 3.5 / 4.0で検証されています


-1

うーん、なぜこのようなプログラミングがすべて行われたのかはわかりません。元の問題は、このデフォルトルートを指定することで簡単に解決できると思います...

routes.MapRoute("Default", "{*id}", 
                 new { controller = "Home"
                     , action = "Index"
                     , id = UrlParameter.Optional 
                     }
              );
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.