Saya memiliki dua metode tindakan yang saling bertentangan. Pada dasarnya, saya ingin bisa mendapatkan tampilan yang sama menggunakan dua rute berbeda, baik dengan ID item atau dengan nama item dan induknya (item dapat memiliki nama yang sama di antara orang tua yang berbeda). Istilah pencarian dapat digunakan untuk memfilter daftar.
Sebagai contoh...
Items/{action}/ParentName/ItemName
Items/{action}/1234-4321-1234-4321
Berikut adalah metode tindakan saya (ada juga Remove
metode tindakan) ...
// Method #1
public ActionResult Assign(string parentName, string itemName) {
// Logic to retrieve item's ID here...
string itemId = ...;
return RedirectToAction("Assign", "Items", new { itemId });
}
// Method #2
public ActionResult Assign(string itemId, string searchTerm, int? page) { ... }
Dan inilah rutenya ...
routes.MapRoute("AssignRemove",
"Items/{action}/{itemId}",
new { controller = "Items" }
);
routes.MapRoute("AssignRemovePretty",
"Items/{action}/{parentName}/{itemName}",
new { controller = "Items" }
);
Saya mengerti mengapa kesalahan terjadi, karena page
parameternya bisa nol, tetapi saya tidak tahu cara terbaik untuk mengatasinya. Apakah desain saya awalnya buruk? Saya telah berpikir tentang memperluas Method #1
tanda tangan untuk menyertakan parameter pencarian dan memindahkan logika Method #2
ke metode pribadi yang akan mereka panggil, tetapi saya tidak percaya itu benar-benar akan menyelesaikan ambiguitas.
Bantuan apa pun akan sangat dihargai.
Solusi Aktual (berdasarkan jawaban Levi's)
Saya menambahkan kelas berikut ...
public class RequireRouteValuesAttribute : ActionMethodSelectorAttribute {
public RequireRouteValuesAttribute(string[] valueNames) {
ValueNames = valueNames;
}
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) {
bool contains = false;
foreach (var value in ValueNames) {
contains = controllerContext.RequestContext.RouteData.Values.ContainsKey(value);
if (!contains) break;
}
return contains;
}
public string[] ValueNames { get; private set; }
}
Dan kemudian mendekorasi metode tindakan ...
[RequireRouteValues(new[] { "parentName", "itemName" })]
public ActionResult Assign(string parentName, string itemName) { ... }
[RequireRouteValues(new[] { "itemId" })]
public ActionResult Assign(string itemId) { ... }
sumber
return ValueNames.All(v => controllerContext.RequestContext.RouteData.Values.ContainsKey(v));
contains = ...
contains = controllerContext.RequestContext.RouteData.Values.ContainsKey(value) || controllerContext.RequestContext.HttpContext.Request.Params.AllKeys.Contains(value);
ActionResult DoSomething(Person p)
manaPerson
memiliki berbagai properti sederhana sepertiName
, dan permintaan untuk itu dibuat dengan nama properti secara langsung (misalnya,/dosomething/?name=joe+someone&other=properties
).controllerContext.HttpContext.Request[value] != null
bukancontrollerContext.RequestContext.RouteData.Values.ContainsKey(value)
; tapi tetap saja pekerjaan yang bagus.Jawaban:
MVC tidak mendukung metode overloading hanya berdasarkan tanda tangan, jadi ini akan gagal:
public ActionResult MyMethod(int someInt) { /* ... */ } public ActionResult MyMethod(string someString) { /* ... */ }
Namun, tidak metode dukungan overloading berdasarkan atribut:
[RequireRequestValue("someInt")] public ActionResult MyMethod(int someInt) { /* ... */ } [RequireRequestValue("someString")] public ActionResult MyMethod(string someString) { /* ... */ } public class RequireRequestValueAttribute : ActionMethodSelectorAttribute { public RequireRequestValueAttribute(string valueName) { ValueName = valueName; } public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { return (controllerContext.HttpContext.Request[ValueName] != null); } public string ValueName { get; private set; } }
Dalam contoh di atas, atribut hanya mengatakan "metode ini cocok jika kunci xxx ada dalam permintaan." Anda juga dapat memfilter berdasarkan informasi yang terdapat dalam rute (controllerContext.RequestContext) jika itu lebih sesuai dengan tujuan Anda.
sumber
...RouteData.Values
, tapi ini "berhasil". Apakah itu pola yang baik atau tidak terbuka untuk diperdebatkan. :)Parameter di rute Anda
{roleId}
,{applicationName}
dan{roleName}
tidak cocok dengan nama parameter dalam metode tindakan Anda. Saya tidak tahu apakah itu penting, tetapi itu membuatnya lebih sulit untuk mengetahui apa niat Anda.Apakah itemId Anda sesuai dengan pola yang dapat dicocokkan melalui regex? Jika demikian, Anda dapat menambahkan pembatas ke rute Anda sehingga hanya url yang cocok dengan pola yang diidentifikasi sebagai berisi itemId.
Jika itemId Anda hanya berisi angka, ini akan berfungsi:
routes.MapRoute("AssignRemove", "Items/{action}/{itemId}", new { controller = "Items" }, new { itemId = "\d+" } );
Sunting: Anda juga bisa menambahkan batasan ke
AssignRemovePretty
rute sehingga keduanya{parentName}
dan{itemName}
dibutuhkan.Sunting 2: Juga, karena tindakan pertama Anda hanya mengarahkan ke tindakan kedua, Anda dapat menghilangkan beberapa ambiguitas dengan mengganti nama yang pertama.
// Method #1 public ActionResult AssignRemovePretty(string parentName, string itemName) { // Logic to retrieve item's ID here... string itemId = ...; return RedirectToAction("Assign", itemId); } // Method #2 public ActionResult Assign(string itemId, string searchTerm, int? page) { ... }
Kemudian tentukan nama Tindakan di rute Anda untuk memaksa metode yang tepat dipanggil:
routes.MapRoute("AssignRemove", "Items/Assign/{itemId}", new { controller = "Items", action = "Assign" }, new { itemId = "\d+" } ); routes.MapRoute("AssignRemovePretty", "Items/Assign/{parentName}/{itemName}", new { controller = "Items", action = "AssignRemovePretty" }, new { parentName = "\w+", itemName = "\w+" } );
sumber
Pendekatan lain adalah dengan mengganti nama salah satu metode agar tidak terjadi konflik. Sebagai contoh
// GET: /Movies/Delete/5 public ActionResult Delete(int id = 0) // POST: /Movies/Delete/5 [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id = 0)
Lihat http://www.asp.net/mvc/tutorials/getting-started-with-mvc3-part9-cs
sumber
Baru-baru ini saya mengambil kesempatan untuk meningkatkan jawaban @ Levi's untuk mendukung berbagai skenario yang harus saya tangani, seperti: dukungan beberapa parameter, cocokkan salah satu dari mereka (bukan semuanya) dan bahkan tidak mencocokkan tidak satu pun dari mereka.
Inilah atribut yang saya gunakan sekarang:
/// <summary> /// Flags an Action Method valid for any incoming request only if all, any or none of the given HTTP parameter(s) are set, /// enabling the use of multiple Action Methods with the same name (and different signatures) within the same MVC Controller. /// </summary> public class RequireParameterAttribute : ActionMethodSelectorAttribute { public RequireParameterAttribute(string parameterName) : this(new[] { parameterName }) { } public RequireParameterAttribute(params string[] parameterNames) { IncludeGET = true; IncludePOST = true; IncludeCookies = false; Mode = MatchMode.All; } public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { switch (Mode) { case MatchMode.All: default: return ( (IncludeGET && ParameterNames.All(p => controllerContext.HttpContext.Request.QueryString.AllKeys.Contains(p))) || (IncludePOST && ParameterNames.All(p => controllerContext.HttpContext.Request.Form.AllKeys.Contains(p))) || (IncludeCookies && ParameterNames.All(p => controllerContext.HttpContext.Request.Cookies.AllKeys.Contains(p))) ); case MatchMode.Any: return ( (IncludeGET && ParameterNames.Any(p => controllerContext.HttpContext.Request.QueryString.AllKeys.Contains(p))) || (IncludePOST && ParameterNames.Any(p => controllerContext.HttpContext.Request.Form.AllKeys.Contains(p))) || (IncludeCookies && ParameterNames.Any(p => controllerContext.HttpContext.Request.Cookies.AllKeys.Contains(p))) ); case MatchMode.None: return ( (!IncludeGET || !ParameterNames.Any(p => controllerContext.HttpContext.Request.QueryString.AllKeys.Contains(p))) && (!IncludePOST || !ParameterNames.Any(p => controllerContext.HttpContext.Request.Form.AllKeys.Contains(p))) && (!IncludeCookies || !ParameterNames.Any(p => controllerContext.HttpContext.Request.Cookies.AllKeys.Contains(p))) ); } } public string[] ParameterNames { get; private set; } /// <summary> /// Set it to TRUE to include GET (QueryStirng) parameters, FALSE to exclude them: /// default is TRUE. /// </summary> public bool IncludeGET { get; set; } /// <summary> /// Set it to TRUE to include POST (Form) parameters, FALSE to exclude them: /// default is TRUE. /// </summary> public bool IncludePOST { get; set; } /// <summary> /// Set it to TRUE to include parameters from Cookies, FALSE to exclude them: /// default is FALSE. /// </summary> public bool IncludeCookies { get; set; } /// <summary> /// Use MatchMode.All to invalidate the method unless all the given parameters are set (default). /// Use MatchMode.Any to invalidate the method unless any of the given parameters is set. /// Use MatchMode.None to invalidate the method unless none of the given parameters is set. /// </summary> public MatchMode Mode { get; set; } public enum MatchMode : int { All, Any, None } }
Untuk info lebih lanjut dan contoh cara penerapan, lihat posting blog yang saya tulis tentang topik ini.
sumber
routes.MapRoute("AssignRemove", "Items/{parentName}/{itemName}", new { controller = "Items", action = "Assign" } );
pertimbangkan untuk menggunakan pustaka rute pengujian MVC Contribs untuk menguji rute Anda
"Items/parentName/itemName".Route().ShouldMapTo<Items>(x => x.Assign("parentName", itemName));
sumber