'Multiple actions were found' RESTful Web API 路由错误

'Multiple actions were found' error with RESTful Web API Routing

我有以下 class:

public class GetLogsRequestDto
{
    public LogLevel Level { get; set; }

    public LogSortOrder SortOrder { get; set; }
}

我有一个 Web API 控制器 (LogsController),具有以下 2 个操作:

async Task<IHttpActionResult> Get( [FromUri]int id )

async Task<IHttpActionResult> Get( [FromUri]GetLogsRequestDto dto )

第一个用于检索特定日志,第二个用于检索日志列表。当我通过以下方式对特定日志发出 GET 请求时:/logs/123,它会正确调用第一个操作,同样,如果我对 /logs 发出 GET 请求,它会正确调用第二个操作([= 中定义的属性45=] 是可选的,不需要总是提供)。

但是,我想更改第一个 GET 方法,使其使用 class 而不是 int id 参数,如下所示(请注意,它为上面的第二个操作指定了不同的(单一)类型):

async Task<IHttpActionResult> Get( [FromUri]GetLogRequestDto dto )

这个 GetLogRequestDto class 看起来像这样:

public class GetLogRequestDto
{
    [Required]
    [Range( 100, int.MaxValue )]
    public int Id { get; set; }
}

我采用这种方法的原因是,我可以通过我的标准 ModelStateValidationActionFilter 对模型进行验证,并将任何特定的验证属性放入此 class,而不是在使用'int id' 参数方法,然后必须执行验证。

当我实施此方法并尝试调用 /logs/1 时,我收到以下错误:

Multiple actions were found that match the request

这两种方法中用作参数的两种不同类型没有区别。

我配置的默认路由是:

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

我不明白为什么会出现问题 - 为什么它以一种方式工作而不是另一种方式。

在 GET 请求中使用复杂类型来处理单个基本类型参​​数(这也是路由的一部分)并不是一个好主意。

通过使用这种方法,框架将无法将您的路由参数绑定到该复杂类型(路由定义需要一个必须是简单类型的 id 参数)。

我强烈建议您恢复更改并再次将 id 参数设为 int

作为替代方法,您可以遵循 this great post 并实施一个操作过滤器,该过滤器可以验证您的方法参数,这些参数由验证属性修饰,即使它们是简单类型也是如此。

这是 Mark Vincze 博客 post 的摘录,表示用于验证操作参数的操作过滤器属性:

public class ValidateActionParametersAttribute : ActionFilterAttribute  
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        var descriptor = context.ActionDescriptor as ControllerActionDescriptor;

        if (descriptor != null)
        {
            var parameters = descriptor.MethodInfo.GetParameters();

            foreach (var parameter in parameters)
            {
                var argument = context.ActionArguments[parameter.Name];

                EvaluateValidationAttributes(parameter, argument, context.ModelState);
            }
        }

        base.OnActionExecuting(context);
    }

    private void EvaluateValidationAttributes(ParameterInfo parameter, object argument, ModelStateDictionary modelState)
    {
        var validationAttributes = parameter.CustomAttributes;

        foreach (var attributeData in validationAttributes)
        {
            var attributeInstance = CustomAttributeExtensions.GetCustomAttribute(parameter, attributeData.AttributeType);

            var validationAttribute = attributeInstance as ValidationAttribute;

            if (validationAttribute != null)
            {
                var isValid = validationAttribute.IsValid(argument);
                if (!isValid)
                {
                    modelState.AddModelError(parameter.Name, validationAttribute.FormatErrorMessage(parameter.Name));
                }
            }
        }
    }
}