当我有一个Post动作时,为什么我会“找到多个动作...”?

时间:2016-06-11 13:14:23

标签: c# asp.net-mvc asp.net-web-api asp.net-web-api2 asp.net-web-api-routing

我的FBMessageController只有以下方法:

public string Get() { ... }

[ChildActionOnly]
public SendResponse Send(ComplexType msg) { ... }

[ChildActionOnly]
public SendResponse SendImage(string x, string y) { ... }

[HttpPost]
public SendResponse Post([FromBody]AnotherComplexType yyy) { ... }

public void Put(..) { ... }

public void Delete(..) { ... }

但是,当我尝试使用POST向.../api/fbMessage发送请求时,

我得到以下异常:

  

“发现与请求匹配的多重操作”

WebApiConfig.Register包含默认代码:

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }

导致错误的原因是什么?

3 个答案:

答案 0 :(得分:1)

如果没有具体定义HTTP方法属性,则默认为POST。所以Send()也被认为是post方法,并且因为找到了多个动作而发生异常。

您可以尝试安装调试路由器工具,以直观地了解控制器和操作的选择方式。这是a link

答案 1 :(得分:0)

你能尝试用这样的属性装饰你的所有动作,看看是否有任何改变

[HttpGet]
public string Get() { ... }

[ChildActionOnly]
public SendResponse Send(..) { ... }

[ChildActionOnly]
public SendResponse SendImage(..) { ... }

[HttpPost]
public SendResponse Post([FromBody]xxx yyy) { ... }

[HttpPut]
public void Put(..) { ... }

[HttpDelete]
public void Delete(..) { ... }

可能是你的Put和Post在某种程度上是冲突的。

答案 2 :(得分:0)

虽然您似乎已经隔离了该问题,但我进行了以下集成测试以重新创建您的问题并帮助解释导致错误的原因。

[TestClass]
public class FBMessageControllerTests {
    [TestMethod]
    public async Task HttpClient_Should_Get_OKStatus_From_Post_To_FBMessage() {

        using (var server = new TestServer()) {

            var config = server.Configuration;
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );


            var handlerMock = new Mock<IExceptionHandler>();
            handlerMock
                .Setup(m => m.HandleAsync(It.IsAny<ExceptionHandlerContext>(), It.IsAny<System.Threading.CancellationToken>()))
                .Callback<ExceptionHandlerContext, CancellationToken>((context, token) => {
                    var innerException = context.ExceptionContext.Exception;

                    Assert.Fail(innerException.Message);
                });
            config.Services.Replace(typeof(IExceptionHandler), handlerMock.Object);



            var client = server.CreateClient();

            string url = "http://localhost/api/fbMessage";

            var body = new { body = "Hello World" };

            using (var response = await client.PostAsJsonAsync(url, body)) {
                var message = await response.Content.ReadAsStringAsync();
                Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, message);
            }
        }
    }

    [Authorize]
    public class FBMessageController : ApiController {
        public string Get() {
            return null;
        }

        [System.Web.Mvc.ChildActionOnly]
        public SendResponse Send(ComplexType test) {
            return null;
        }

        [System.Web.Mvc.ChildActionOnly]
        public SendResponse SendImage(string x, string y) {
            return null;
        }

        [HttpPost]
        public SendResponse Post([FromBody]AnotherComplexType body) {
            return null;
        }

        public void Put(string gbody) {
            return;
        }

        public void Delete(string body) {
            return;
        }
    }

    public class SendResponse { }
    public class ComplexType {  }
    public class AnotherComplexType {  }
}

其中产生了以下结果消息:

  

找到符合请求的多项操作:
  发送类型MiscUnitTests + FBMessageControllerTests + FBMessageController
  发布类型MiscUnitTests + FBMessageControllerTests + FBMessageController

以上消息可以准确地告诉您导致问题的原因。该框架通常会告诉您为什么会遇到某些错误。有时你必须知道在哪里看。

引用以下Routing and Action Selection in ASP.NET Web API,这就是您遇到问题的原因。

  

这是动作选择算法。

     
      
  1. 创建控制器上与HTTP请求方法匹配的所有操作的列表。
  2.   
  3. 如果路线词典有&#34;动作&#34;输入,删除名称与此值不匹配的操作。
  4.   
  5. 尝试将操作参数与URI匹配,如下所示:      
        
    • 对于每个操作,获取一个简单类型的参数列表,其中绑定从URI获取参数。   排除可选参数。
    •   
    • 从此列表中,尝试在路径字典或URI查询字符串中查找每个参数名称的匹配项。比赛是   不区分大小写,不依赖于参数顺序。
    •   
    • 选择一个操作,其中列表中的每个参数都与URI匹配。
    •   
    • 如果多一个操作符合这些条件,请选择参数匹配最多的一个。
    •   
  6.   
  7. 使用[NonAction]属性忽略操作。
  8.         

    步骤#3可能是最令人困惑的。基本的想法是a   参数可以从请求中的URI获取其值   正文,或来自自定义绑定。对于来自URI的参数,   我们希望确保URI实际包含该值   参数,在路径中(通过路由字典)或在路径中   查询字符串。

         

    例如,请考虑以下操作:

    public void Get(int id)
    
         

    id参数绑定到URI。因此,这个动作只能   在路由中匹配包含&#34; id&#34;的值的URI   字典或查询字符串。

         

    可选参数是一个例外,因为它们是可选的。对于   一个可选参数,如果绑定无法从中获取值,则表示没问题   URI。

         

    复杂类型是一个例外,原因不同。复杂的类型   只能通过自定义绑定绑定到URI。但在那种情况下,   框架无法预先知道参数是否会绑定   到特定的URI。要找出它,需要调用绑定。   选择算法的目标是从中选择一个动作   静态描述,在调用任何绑定之前。因此,复杂   类型从匹配算法中排除。

         

    选择操作后,将调用所有参数绑定。

         

    要点:

         
        
    • 操作必须与请求的HTTP方法匹配。
    •   
    • 操作名称必须与&#34;操作&#34;匹配。路线词典中的条目(如果存在)。
    •   
    • 对于操作的每个参数,如果参数取自URI,则必须在路径中找到参数名称   字典或URI查询字符串。 (可选参数和   排除复杂类型的参数。)
    •   
    • 尝试匹配最多的参数。最佳匹配可能是没有参数的方法。
    •   

希望这可以帮助您理解为什么您会收到“发现多个操作...”消息。

快乐的编码!!!