Cara mengatur Rute Default (Ke suatu Area) di MVC


122

Oke, ini telah ditanyakan sebelumnya tetapi tidak ada solusi pasti di luar sana. Jadi untuk tujuan saya sendiri dan orang lain yang mungkin menganggap ini berguna.

Di MVC2 (ASP.NET) saya menginginkannya sehingga ketika seseorang menavigasi ke situs web, ada area default yang ditentukan. Jadi menavigasi ke situs saya akan mengarahkan Anda ke ControllerX ActionY di AreaZ.

Menggunakan rute berikut di Global.asax

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

Sekarang ini berfungsi seperti di dalamnya mencoba untuk melayani halaman yang benar. Namun MVC melanjutkan untuk mencari tampilan di root situs dan bukan di folder Area.

Apakah ada cara untuk mengatasi ini?

EDIT

Ada 'Solusi' dan itu ada di ControllerX, ActionY mengembalikan jalur lengkap tampilan. Sedikit hack tetapi berhasil. Namun saya berharap ada solusi yang lebih baik.

         public ActionResult ActionY()
        {
            return View("~/Areas/AreaZ/views/ActionY.aspx");
        }

Edit:

Ini juga menjadi masalah saat memiliki HTML ActionLink halaman. Jika area tidak diatur, Action Link adalah keluaran kosong.

Apakah semua ini disengaja atau cacat?

Jawaban:


98

Yang ini membuatku tertarik, dan akhirnya aku punya kesempatan untuk memeriksanya. Orang lain tampaknya belum mengerti bahwa ini adalah masalah dengan menemukan tampilan , bukan masalah dengan perutean itu sendiri - dan itu mungkin karena judul pertanyaan Anda menunjukkan bahwa ini tentang perutean.

Bagaimanapun, karena ini adalah masalah terkait Tampilan, satu-satunya cara untuk mendapatkan yang Anda inginkan adalah dengan mengganti mesin tampilan default . Biasanya, saat Anda melakukan ini, ini untuk tujuan sederhana untuk mengalihkan mesin tampilan Anda (yaitu ke Spark, NHaml, dll.). Dalam kasus ini, ini bukan logika pembuatan tampilan yang perlu kita ganti, tetapi metode FindPartialViewdan FindViewdi VirtualPathProviderViewEnginekelas.

Anda dapat berterima kasih kepada bintang keberuntungan Anda bahwa metode ini sebenarnya virtual, karena semua yang lain di dalam VirtualPathProviderViewEnginebahkan tidak dapat diakses - ini pribadi, dan itu membuatnya sangat menjengkelkan untuk menimpa logika find karena pada dasarnya Anda harus menulis ulang setengah dari kode yang sudah ada. telah ditulis jika Anda ingin bermain bagus dengan cache lokasi dan format lokasi. Setelah beberapa penggalian di Reflector, saya akhirnya berhasil menemukan solusi yang berhasil.

Apa yang telah saya lakukan di sini adalah pertama-tama membuat abstrak AreaAwareViewEngineyang diturunkan langsung dari, VirtualPathProviderViewEnginebukan WebFormViewEngine. Saya melakukan ini sehingga jika Anda ingin membuat tampilan Spark sebagai gantinya (atau apa pun), Anda masih dapat menggunakan kelas ini sebagai tipe dasar.

Kode di bawah ini cukup bertele-tele, jadi untuk memberi Anda ringkasan singkat tentang apa yang sebenarnya dilakukannya: Ini memungkinkan Anda memasukkan a {2}ke dalam format lokasi, yang sesuai dengan nama area, dengan cara yang sama {1}sesuai dengan nama pengontrol. Itu dia! Untuk itulah kami harus menulis semua kode ini:

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;
    }
}

Seperti yang dinyatakan, ini bukan mesin beton, jadi Anda harus membuatnya juga. Bagian ini, untungnya, jauh lebih mudah, yang perlu kita lakukan hanyalah mengatur format default dan benar-benar membuat tampilan:

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);
    }
}

Perhatikan bahwa kami telah menambahkan beberapa entri ke standar ViewLocationFormats. Ini adalah {2}entri baru , di mana {2}akan dipetakan ke yang areakita masukkan ke dalam RouteData. Saya telah meninggalkannya MasterLocationFormatssendiri, tetapi jelas Anda dapat mengubahnya jika Anda mau.

Sekarang modifikasi Anda global.asaxuntuk mendaftarkan mesin tampilan ini:

Global.asax.cs

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);
    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new AreaAwareViewEngine());
}

... dan daftarkan rute default:

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 = "" }
    );
}

Sekarang Buat yang AreaControllerbaru saja kita referensikan:

DefaultController.cs (dalam ~ / Controllers /)

public class DefaultController : Controller
{
    public ActionResult ActionY()
    {
        return View("TestView");
    }
}

Jelas kami membutuhkan struktur direktori dan tampilan untuk menyertainya - kami akan membuat ini sangat sederhana:

TestView.aspx (di ~ / Area / AreaZ / Views / Default / atau ~ / Area / AreaZ / Views / Shared /)

<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<h2>TestView</h2>
This is a test view in AreaZ.

Dan itu dia. Akhirnya, kami selesai .

Untuk sebagian besar, Anda seharusnya bisa mengambil BaseAreaAwareViewEnginedan AreaAwareViewEnginedan meletakkannya ke dalam proyek MVC mana pun, jadi meskipun butuh banyak kode untuk menyelesaikannya, Anda hanya perlu menulisnya sekali. Setelah itu, tinggal mengedit beberapa baris global.asax.csdan membuat struktur situs Anda.


Ini kemungkinan besar merupakan solusi terbaik saat ini tetapi jauh dari ideal. Seperti di atas setelah Anda menambahkan Actionlink atau masalah yang sama muncul.
LiamB

1
@ Pino: Saya pikir Anda seharusnya dapat memecahkan ActionLinkmasalah dengan menambahkan hal yang sama area = "AreaZ"ke pemetaan rute "Default" di global.asax.cs. Saya tidak yakin; coba dan lihat.
Aaronaught

Dalam MVC4, deklarasi rute "Default" dipindahkan dari Global.asax ke ~ / App_Start / RouteConfig.cs / RegisterRoutes ()
Andriy F.

3
Saya benci untuk memberikan suara negatif, tetapi saya benar-benar tidak percaya jawaban di bawah oleh @Chris Alderson belum menerima lebih banyak suara. Ini adalah solusi yang jauh lebih sederhana daripada yang ini dan tampaknya menyelesaikan kasus tepi (ActionLinks, dll).
jdmcnair

Sepertinya ada bug di sini. Tampilan untuk Area bernama "Re" misalnya akan berada di ~ / Area / Re / Views / Ctrlr / blah.aspx, tetapi kode di sini menggunakan ~ / {2} / {1} / {0} yang akan menjadi ~ /Re/Ctrl/blah.aspx, kehilangan direktori Area kritis di jalur. Seharusnya "~ / Area / {2} / Views / {1} / {0} .aspx"
Chris Moschini

100

Beginilah cara saya melakukannya. Saya tidak tahu mengapa MapRoute () tidak mengizinkan Anda menyetel area, tetapi mengembalikan objek rute sehingga Anda dapat terus membuat perubahan tambahan yang Anda inginkan. Saya menggunakan ini karena saya memiliki situs MVC modular yang dijual kepada pelanggan perusahaan dan mereka harus dapat memasukkan dll ke dalam folder bin untuk menambahkan modul baru. Saya mengizinkan mereka untuk mengubah "HomeArea" di konfigurasi AppSettings.

var route = routes.MapRoute(
                "Home_Default", 
                "", 
                new {controller = "Home", action = "index" },
                new[] { "IPC.Web.Core.Controllers" }
               );
route.DataTokens["area"] = area;

Sunting: Anda dapat mencoba ini juga di AreaRegistration.RegisterArea untuk area yang Anda inginkan agar pengguna pergi secara default. Saya belum mengujinya tetapi AreaRegistrationContext.MapRoute melakukan set route.DataTokens["area"] = this.AreaName;untuk Anda.

context.MapRoute(
                    "Home_Default", 
                    "", 
                    new {controller = "Home", action = "index" },
                    new[] { "IPC.Web.Core.Controllers" }
                   );

Berhasil. Berhati-hatilah dengan file web.config baru, ini dapat menimpa konfigurasi global lama Anda.
Mert Akcakaya

56

bahkan sudah dijawab - ini adalah sintaks singkat (ASP.net 3, 4, 5):

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

6
Ini bagus untuk saya. Saya tidak memiliki pengontrol di root dan hanya menggunakan Area. Untuk MVC 4 saya memiliki ini menggantikan default di RouteConfig.cs. Terima kasih!
Marc

2
Saya menggunakan MVC4 dan ini adalah solusi paling sederhana untuk saya. Mengizinkan aplikasi menggunakan tampilan Indeks dalam Area tertentu sebagai 'beranda' situs.
JTech

2
Solusi ini tidak akan berfungsi di masa mendatang (dari Asp.Net MVC6 ke atas).
Patrick Desjardins

@ PatrickDesjardins: Ada alasan untuk tidak mendukung solusi di atas?
Akash KC

@SeriousM Jawaban Anda selalu hijau. Itu masih membantu. Anda menyelamatkan saya malam.
skpaul

16

Terima kasih kepada Aaron karena telah menunjukkan bahwa ini tentang menemukan pemandangan, saya salah paham.

[PEMBARUAN] Saya baru saja membuat proyek yang mengirim pengguna ke Area secara default tanpa mengotak-atik kode atau jalur pencarian:

Di global.asax, daftar seperti biasa:

    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,
        );
    }

di Application_Start(), pastikan untuk menggunakan urutan berikut;

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        RegisterRoutes(RouteTable.Routes);
    }

dalam pendaftaran area Anda, gunakan

    public override void RegisterArea(AreaRegistrationContext context)
    {
        context.MapRoute(
            "ShopArea_default",
            "{controller}/{action}/{id}",
            new { action = "Index", id = "", controller = "MyRoute" },
            new { controller = "MyRoute" }
        );
    }

Contohnya dapat ditemukan di http://www.emphess.net/2010/01/31/areas-routes-and-defaults-in-mvc-2-rc/

Saya sangat berharap ini yang Anda minta ...

////

Saya tidak berpikir bahwa menulis pseudo ViewEngineadalah solusi terbaik dalam kasus ini. (Kurang reputasi, saya tidak bisa berkomentar). The WebFormsViewEnginemenyadari Lokasi dan berisi AreaViewLocationFormatsyang didefinisikan secara default sebagai

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",
    };

Saya yakin Anda tidak mematuhi konvensi ini. Anda memposting

public ActionResult ActionY() 
{ 
    return View("~/Areas/AreaZ/views/ActionY.aspx"); 
} 

sebagai peretasan yang berfungsi, tetapi seharusnya begitu

   return View("~/Areas/AreaZ/views/ControllerX/ActionY.aspx"); 

JIKA Anda tidak ingin mengikuti konvensi, Anda mungkin ingin mengambil jalur singkat dengan mengambil dari WebFormViewEngine(yang dilakukan di MvcContrib, misalnya) di mana Anda bisa menyetel jalur pencarian di konstruktor, atau -a sedikit hacky- dengan menentukan konvensi Anda seperti ini di Application_Start:

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

Ini harus dilakukan dengan sedikit lebih hati-hati, tentu saja, tapi menurut saya ini menunjukkan idenya. Bidang-bidang ini ada publicdi VirtualPathProviderViewEnginedalam MVC 2 RC.


Perlu dicatat bahwa ini hanya berlaku di MVC 2 RC - MVC 1 VirtualPathProviderViewEnginetidak memiliki properti ini dan tidak peka area. Dan sementara pertanyaan ini memang dinyatakan tentang MVC 2, banyak orang masih tidak menggunakannya (dan tidak akan untuk beberapa waktu). Jadi, jawaban Anda lebih mudah untuk pertanyaan spesifik, tetapi jawaban saya adalah satu-satunya yang akan bekerja untuk pengguna MVC1 yang menemukan pertanyaan ini. Saya ingin memberikan jawaban yang tidak bergantung pada fungsionalitas pra-rilis yang berpotensi dapat berubah.
Aaronaught

Juga bukan "pseudo view engine" - kelas mesin tampilan sengaja dibuat agar dapat diperluas sehingga berbagai jenis tampilan dapat digunakan.
Aaronaught

Itu tidak dimaksudkan untuk menghina Anda, saya minta maaf. Ini adalah 'pseudo' karena tidak secara signifikan mengubah cara penanganan View, tetapi hanya menggantikan beberapa nilai.
mnemosyn

Saya tidak tersinggung, saya hanya ingin menjelaskan fakta bahwa ini bukan alasan yang tidak biasa untuk mendapatkan mesin tampilan khusus, sebagaimana dibuktikan oleh fakta bahwa metode yang relevan dapat diganti.
Aaronaught

2
Tip bagus tentang RegisterAreaspergi sebelumnya RegisterRoutes. Bertanya-tanya mengapa kode saya tiba-tiba berhenti bekerja dan memperhatikan refactor itu;)
webnoob

6

Saya rasa Anda ingin pengguna dialihkan ke ~/AreaZURL setelah dia mengunjungi ~/URL. Saya akan mencapai melalui kode berikut di root Anda HomeController.

public class HomeController
{
    public ActionResult Index()
    {
        return RedirectToAction("ActionY", "ControllerX", new { Area = "AreaZ" });
    }
}

Dan mengikuti rute masuk Global.asax.

routes.MapRoute(
    "Redirection to AreaZ",
    String.Empty,
    new { controller = "Home ", action = "Index" }
);

Ini berfungsi, tetapi berubah menjadi URL di browser pengguna. Tidak terlalu ideal.
LiamB

2

Pertama, versi MVC2 apa yang Anda gunakan? Ada perubahan signifikan dari preview2 ke RC.

Dengan asumsi Anda menggunakan RC, saya pikir pemetaan rute Anda harus terlihat berbeda. Di wilayah AreaRegistration.csAnda, Anda dapat mendaftarkan beberapa jenis rute default, misalnya

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

Kode di atas akan mengirim pengguna ke MyRouteControllerdalam ShopAreaper default kami.

Menggunakan string kosong sebagai parameter kedua harus memunculkan pengecualian, karena pengontrol harus ditentukan.

Tentu saja Anda harus mengubah rute default Global.asaxagar tidak mengganggu rute default ini, misalnya dengan menggunakan prefiks untuk situs utama.

Juga lihat utas ini dan jawaban Haack: MVC 2 AreaRegistration Routes Order

Semoga ini membantu.


Terima kasih, tapi saya tidak yakin ini menyelesaikan masalah yang dijelaskan dalam pertanyaan. Dan MVC RC
LiamB

2

Menambahkan berikut ini ke Application_Start saya berfungsi untuk saya, meskipun saya tidak yakin apakah Anda memiliki pengaturan ini di 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

Apa yang saya lakukan agar ini berhasil adalah sebagai berikut:

  1. Saya membuat pengontrol default di folder root / Controllers. Saya menamai pengontrol saya DefaultController.
  2. Di pengontrol saya menambahkan kode berikut:

    namespace MyNameSpace.Controllers {
    public class DefaultController : Controller {
        // GET: Default
        public ActionResult Index() {
            return RedirectToAction("Index", "ControllerName", new {area = "FolderName"});
        }
    } }
  3. Di RouterConfig.cs saya, saya menambahkan yang berikut ini:

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

Trik di balik semua ini adalah saya membuat konstruktor default yang akan selalu menjadi pengontrol startup setiap kali aplikasi saya dimulai. Ketika mencapai pengontrol default itu, itu akan mengarahkan ke pengontrol apa pun yang saya tentukan dalam Tindakan Indeks default. Yang mana dalam kasus saya

www.myurl.com/FolderName/ControllerName

.


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

Sudahkah kamu mencobanya?


Ya, masalahnya terletak pada fakta bahwa sekarang situs tersebut mencari tampilan di root. Tampilan 'ActionY' atau masternya tidak ditemukan. Lokasi berikut ditelusuri: ~ / Views / ActionY / ActionY.aspx ~ / Views / ActionY / ActionY.ascx ~ / Views / Shared / ActionY.aspx ~ / Views / Shared / ActionY.ascx
LiamB

2
Saya mengerti. Saya akan mencoba mencari solusi. +1 untuk pertanyaan
Barbaros Alp

0

Menemukan lokasi blok penyusun yang berbeda dilakukan dalam siklus hidup permintaan. Salah satu langkah pertama dalam siklus hidup permintaan ASP.NET MVC adalah memetakan URL yang diminta ke metode tindakan pengontrol yang benar. Proses ini disebut sebagai routing. Rute default diinisialisasi di file Global.asax dan menjelaskan kerangka kerja ASP.NET MVC cara menangani permintaan. Mengklik dua kali file Global.asax di proyek MvcApplication1 akan menampilkan kode berikut:

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);
       }
   }

}

Dalam event handler Application_Start (), yang dijalankan setiap kali aplikasi dikompilasi atau server web di-restart, tabel rute didaftarkan. Rute default diberi nama Default, dan merespons URL dalam bentuk http://www.example.com/ {controller} / {action} / {id}. Variabel antara {dan} diisi dengan nilai sebenarnya dari URL permintaan atau dengan nilai default jika tidak ada penggantian di URL. Rute default ini akan dipetakan ke pengontrol Beranda dan ke metode tindakan Indeks, sesuai dengan parameter perutean default. Kami tidak akan melakukan tindakan lain dengan peta perutean ini.

Secara default, semua kemungkinan URL dapat dipetakan melalui rute default ini. Juga memungkinkan untuk membuat rute kita sendiri. Misalnya, mari petakan URL http://www.example.com/Employee/Maarten ke pengontrol Karyawan, tindakan Tampilkan, dan parameter nama depan. Potongan kode berikut dapat disisipkan ke dalam file Global.asax yang baru saja kita buka. Karena kerangka kerja ASP.NET MVC menggunakan rute pencocokan pertama, potongan kode ini harus disisipkan di atas rute default; jika tidak, rute tersebut tidak akan pernah digunakan.

routes.MapRoute(

   "EmployeeShow",                    // Route name
   "Employee/{firstname}",            // URL with parameters
    new {                             // Parameter defaults
       controller = "Employee",
       action = "Show", 
       firstname = "" 
   }  

);

Sekarang, mari tambahkan komponen yang diperlukan untuk rute ini. Pertama-tama, buat kelas bernama EmployeeController di folder Controllers. Anda dapat melakukan ini dengan menambahkan item baru ke proyek dan memilih template Kelas Pengontrol MVC yang terletak di bawah Web | Kategori MVC. Hapus metode tindakan Indeks, dan ganti dengan metode atau tindakan bernama Show. Metode ini menerima parameter nama depan dan meneruskan data ke kamus ViewData. Kamus ini akan digunakan oleh tampilan untuk menampilkan data.

Kelas EmployeeController akan meneruskan objek Employee ke tampilan. Kelas Karyawan ini harus ditambahkan di folder Model (klik kanan pada folder ini dan kemudian pilih Tambah | Kelas dari menu konteks). Berikut kode untuk kelas Karyawan:

namespace MvcApplication1.Models {

   public class Employee
   {
       public string FirstName { get; set; }
       public string LastName { get; set; }
       public string Email { get; set; }
   }

} 

1
Terima kasih, saya tidak begitu yakin bagaimana ini berhubungan dengan pengaturan AREA default. : - /
LiamB

0

Nah, meskipun membuat mesin tampilan khusus dapat berfungsi untuk ini, Anda masih dapat memiliki alternatif:

  • Putuskan apa yang ingin Anda tampilkan secara default.
  • Sesuatu itu memiliki pengontrol dan tindakan (dan Area), bukan?
  • Buka pendaftaran Area itu dan tambahkan sesuatu seperti ini:
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
}

Bersulang!


Sepakat. Meskipun menurut saya tempat yang lebih tepat untuk definisi rute ini adalah di file Global.asax.
nuhusky2003

Dalam kasus seperti itu, definisi global.asax Anda akan mengetahui tentang keberadaan ruang nama pengontrol area, yang menurut saya tidak benar. Area adalah fungsionalitas tambahan, yang berarti Anda harus dapat menambahkan / menghapus satu tanpa menyentuh definisi global.asax. Dalam pendekatan saya terhadap pertanyaan tersebut, saya lebih memilih area untuk "mengambil alih" permintaan, daripada situs [global] untuk "menyerahkan" permintaan.
Tengiz

0

Solusi yang diterima untuk pertanyaan ini adalah, meskipun benar dalam merangkum cara membuat mesin tampilan kustom, tidak menjawab pertanyaan dengan benar. Masalahnya di sini adalah bahwa Pino salah menentukan rute defaultnya . Khususnya definisi "area" nya salah. "Area" diperiksa melalui pengumpulan Token Data dan harus ditambahkan seperti itu:

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);

"Area" yang ditentukan dalam objek default akan diabaikan . Kode di atas membuat rute default, yang menangkap permintaan ke root situs Anda dan kemudian memanggil pengontrol default, Tindakan indeks di area Admin. Harap perhatikan juga bahwa kunci "Namespaces" ditambahkan ke DataTokens, ini hanya diperlukan jika Anda memiliki beberapa pengontrol dengan nama yang sama. Solusi ini diverifikasi dengan Mvc2 dan Mvc3 .NET 3.5 / 4.0


-1

ummm, saya tidak tahu mengapa semua pemrograman ini, saya pikir masalah asli diselesaikan dengan mudah dengan menentukan rute default ini ...

routes.MapRoute("Default", "{*id}", 
                 new { controller = "Home"
                     , action = "Index"
                     , id = UrlParameter.Optional 
                     }
              );
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.