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 FindPartialView
dan FindView
di VirtualPathProviderViewEngine
kelas.
Anda dapat berterima kasih kepada bintang keberuntungan Anda bahwa metode ini sebenarnya virtual, karena semua yang lain di dalam VirtualPathProviderViewEngine
bahkan 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 AreaAwareViewEngine
yang diturunkan langsung dari, VirtualPathProviderViewEngine
bukan 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 area
kita masukkan ke dalam RouteData
. Saya telah meninggalkannya MasterLocationFormats
sendiri, tetapi jelas Anda dapat mengubahnya jika Anda mau.
Sekarang modifikasi Anda global.asax
untuk 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 AreaController
baru 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 BaseAreaAwareViewEngine
dan AreaAwareViewEngine
dan 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.cs
dan membuat struktur situs Anda.
ActionLink
masalah dengan menambahkan hal yang samaarea = "AreaZ"
ke pemetaan rute "Default" diglobal.asax.cs
. Saya tidak yakin; coba dan lihat.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.
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.sumber
bahkan sudah dijawab - ini adalah sintaks singkat (ASP.net 3, 4, 5):
sumber
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:
di
Application_Start()
, pastikan untuk menggunakan urutan berikut;dalam pendaftaran area Anda, gunakan
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
ViewEngine
adalah solusi terbaik dalam kasus ini. (Kurang reputasi, saya tidak bisa berkomentar). TheWebFormsViewEngine
menyadari Lokasi dan berisiAreaViewLocationFormats
yang didefinisikan secara default sebagaiSaya yakin Anda tidak mematuhi konvensi ini. Anda memposting
sebagai peretasan yang berfungsi, tetapi seharusnya begitu
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 diApplication_Start
:Ini harus dilakukan dengan sedikit lebih hati-hati, tentu saja, tapi menurut saya ini menunjukkan idenya. Bidang-bidang ini ada
public
diVirtualPathProviderViewEngine
dalam MVC 2 RC.sumber
VirtualPathProviderViewEngine
tidak 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.RegisterAreas
pergi sebelumnyaRegisterRoutes
. Bertanya-tanya mengapa kode saya tiba-tiba berhenti bekerja dan memperhatikan refactor itu;)Saya rasa Anda ingin pengguna dialihkan ke
~/AreaZ
URL setelah dia mengunjungi~/
URL. Saya akan mencapai melalui kode berikut di root AndaHomeController
.Dan mengikuti rute masuk
Global.asax
.sumber
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.cs
Anda, Anda dapat mendaftarkan beberapa jenis rute default, misalnyaKode di atas akan mengirim pengguna ke
MyRouteController
dalamShopArea
per default kami.Menggunakan string kosong sebagai parameter kedua harus memunculkan pengecualian, karena pengontrol harus ditentukan.
Tentu saja Anda harus mengubah rute default
Global.asax
agar 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.
sumber
Menambahkan berikut ini ke Application_Start saya berfungsi untuk saya, meskipun saya tidak yakin apakah Anda memiliki pengaturan ini di RC:
sumber
Apa yang saya lakukan agar ini berhasil adalah sebagai berikut:
Di pengontrol saya menambahkan kode berikut:
Di RouterConfig.cs saya, saya menambahkan yang berikut ini:
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
.
sumber
Sudahkah kamu mencobanya?
sumber
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:
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.
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:
sumber
Nah, meskipun membuat mesin tampilan khusus dapat berfungsi untuk ini, Anda masih dapat memiliki alternatif:
Bersulang!
sumber
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:
"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
sumber
ummm, saya tidak tahu mengapa semua pemrograman ini, saya pikir masalah asli diselesaikan dengan mudah dengan menentukan rute default ini ...
sumber