解决MVC2里不能使用相同Action名字的问题(与HttpVerbs无关)
来源:互联网 发布:淘宝营销工具有哪些 编辑:程序博客网 时间:2024/04/30 22:08
ASP.NET MVC 2 and the Ambiguous Match Exception for Action methods with different signatures
One frustration I have with ASP.NET MVC is that you can’t easily have two actions with the same name but with different parameters, e.g. Index(int a, int b)
, and Index (int a)
. If you try this you will get an AmbiguousMatchException
because it makes no attempt to match the form values with the method parameters to figure out which method you want to call. Now you can decorate the Index()
with [AcceptVerbs(HttpVerbs.Get)]
so that it at least will not be competing for ASP.NET MVC’s attention during the form post but your other two index methods will still cause the exception.
Supposed you wanted a page /Home/Index that had two forms on it:-
<%using (Html.BeginForm()) { %>
<%=Html.TextBox(“a”) %>
<input type=”submit” name=”submitOne” title=”click me” />
<%} %>
<%using (Html.BeginForm()) { %>
<%=Html.TextBox(“a”) %>
<%=Html.TextBox(“b”) %>
<input type=”submit” name=”submitTwo” title=”click me” />
<%} %>
What we’d like to do is be able to have three action methods, Index()
, Index(Int a)
and Index (Int a, Int b)
.
So let’s define our action methods like that and add a filter attribute to them that will filter methods according to the posted values ignoring any for which there aren’t enough posted values to match the number of parameters or for which the parameter names don’t match.
30 /// <summary>
31 /// Post a single integer back to the form, but don’t allow url /Home/Index/23
32 /// </summary>
33 [ParametersMatch]
34 [AcceptVerbs(HttpVerbs.Post)]
35 public ActionResult Index([FormValue]int a)
36 {
37 ViewData["Message"] = “You supplied one value “ + a ;
38
39 return View();
40 }
41
42 /// <summary>
43 /// Post two integers back to the form OR include two integers in the path
44 /// </summary>
45 [ParametersMatch]
46 [AcceptVerbs(HttpVerbs.Post | HttpVerbs.Get)]
47 public ActionResult Index([FormValue]int a, [FormValue]int b)
48 {
49 ViewData["Message"] = “You supplied two values “ + a + ” “ + b;
50
51 return View();
52 }
And finally, here’s the code that makes that possible: an attribute you can apply to a method parameter to indicate that you want it in the posted form, and an action filter that filters out any action methods that don’t match.
With this in place you can (i) avoid an unnecessary redirect and (ii) have actions with the same name but with different parameters.
Create a new ActionFilter like this …
1 namespace TestApplication.Controllers
2 {
3 using System;
4 using System.Collections.Generic;
5 using System.Collections.ObjectModel;
6 using System.Diagnostics.CodeAnalysis;
7 using System.Linq;
8 using System.Reflection;
9 using System.Web.Mvc.Resources;
10 using System.Web.Mvc;
11 using System.Diagnostics;
12
13
14 /// <summary>
15 /// This attribute can be placed on a parameter of an action method that should be present on the URL in route data
16 /// </summary>
17 [AttributeUsage(AttributeTargets.Parameter, AllowMultiple=false)]
18 public sealed class RouteValueAttribute : Attribute
19 {
20 public RouteValueAttribute() { }
21 }
22
23 /// <summary>
24 /// This attribute can be placed on a parameter of an action method that should be present in FormData
25 /// </summary>
26 [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
27 public sealed class FormValueAttribute : Attribute
28 {
29 public FormValueAttribute() { }
30 }
31
32
33 /// <summary>
34 /// Parameters Match Attribute allows you to specify that an action is only valid
35 /// if it has the right number of parameters marked [RouteValue] or [FormValue] that match with the form data or route data
36 /// </summary>
37 /// <remarks>
38 /// This attribute allows you to have two actions with the SAME name distinguished by the values they accept according to the
39 /// name of those values. Does NOT handle complex types and bindings yet but could be easily adapted to do so.
40 /// </remarks>
41 [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
42 public sealed class ParametersMatchAttribute : ActionMethodSelectorAttribute
43 {
44 public ParametersMatchAttribute() { }
45
46 public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
47 {
48 // The Route values
49 List<string> requestRouteValuesKeys = controllerContext.RouteData.Values.Where(v => !(v.Key == “controller” || v.Key == “action” || v.Key == “area”)).Select(rv => rv.Key).ToList();
50
51 // The Form values
52 var form = controllerContext.HttpContext.Request.Form;
53 List<string> requestFormValuesKeys = form.AllKeys.ToList();
54
55 // The parameters this method expects
56 var parameters = methodInfo.GetParameters();
57
58 // Parameters from the method that we haven’t matched up against yet
59 var parametersNotMatched = parameters.ToList();
60
61 // each parameter of the method can be marked as a [RouteValue] or [FormValue] or both or nothing
62 foreach (var param in parameters)
63 {
64 string name = param.Name;
65
66 bool isRouteParam = param.GetCustomAttributes(true).Any(a => a is RouteValueAttribute);
67 bool isFormParam = param.GetCustomAttributes(true).Any(a => a is FormValueAttribute);
68
69 if (isRouteParam && requestRouteValuesKeys.Contains(name))
70 {
71 // Route value matches parameter
72 requestRouteValuesKeys.Remove(name);
73 parametersNotMatched.Remove(param);
74 }
75 else if (isFormParam && requestFormValuesKeys.Contains(name))
76 {
77 // Form value matches method parameter
78 requestFormValuesKeys.Remove(name);
79 parametersNotMatched.Remove(param);
80 }
81 else
82 {
83 // methodInfo parameter does not match a route value or a form value
84 Debug.WriteLine(methodInfo + ” failed to match “ + param + ” against either a RouteValue or a FormValue”);
85 return false;
86 }
87 }
88
89 // Having removed all the parameters of the method that are matched by either a route value or a form value
90 // we are now left with all the parameters that do not match and all the route and form values that were not used
91
92 if (parametersNotMatched.Count > 0)
93 {
94 Debug.WriteLine(methodInfo + ” – FAIL: has parameters left over not matched by route or form values”);
95 return false;
96 }
97
98 if (requestRouteValuesKeys.Count > 0)
99 {
100 Debug.WriteLine(methodInfo + ” – FAIL: Request has route values left that aren’t consumed”);
101 return false;
102 }
103
104 if (requestFormValuesKeys.Count > 1)
105 {
106 Debug.WriteLine(methodInfo + ” – FAIL : unmatched form values “ + string.Join(“, “, requestFormValuesKeys.ToArray()));
107 return false;
108 }
109
110 Debug.WriteLine(methodInfo + ” – PASS – unmatched form values “ + string.Join(“, “, requestFormValuesKeys.ToArray()));
111 return true;
112 }
113 }
114 }
- 解决MVC2里不能使用相同Action名字的问题(与HttpVerbs无关)
- PowerDesigner 解决不能使用相同字段名字
- ASP.NET MVC2 如何 使用 Profile 的问题及解决
- struts2的Action名字的大小写问题
- 查看废纸篓里的文件,因为发现文件夹里有无数个名字相同的子文件夹,这时候提示名称太长,卷宗无效,具体解决:
- 解决Ubuntu里scim不能随机启动的问题
- 解决qt不能在其他文件里运行的问题
- MVC2中出现一个相同的键已被添加的问题原因
- 使neutron不能创建相同名字以及相同cidr的子网
- VS2005不能调试的问题与解决
- 使用Action创建一个JButton后,改变该按钮的名字可能会遇到意想不到的问题
- MVC2 与 JQuery 的 Ajax 实现局部刷新的问题
- MVC2 中的Action link产生的方式
- 关于 Myeclipse Struts在 配置文件里不能建立Action的问题
- 解决不能使用JSTL标签的问题
- 解决开源库不能使用arc的问题
- 解决脚本不能使用onchange的问题
- 问题:关于Ajax.BeginForm MVC3与MVC2的区别
- 版本控制软件
- VC一点通:实现文件夹的缩略图显示
- 此版本的 SQL Server 不支持用户实例登录标志。该连接将关闭“的解决(转)
- DateDiff Dateadd DateDiff: SQL server函数
- GTD矩阵测试—你觉得自己在哪个象限?
- 解决MVC2里不能使用相同Action名字的问题(与HttpVerbs无关)
- for clever101
- Perl 基本语法
- SetTimer和KillTimer
- 在Ring3上实现文件碎甲(解锁)功能
- ClearQuest与MS Project 集成
- lucene3.0代码实例(一)
- Simplifying the WiX v3 language
- flex security issue