“RESTful Web API路由”发现“发现多个操作”错误

时间:2016-05-17 09:35:14

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

我有以下课程:

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

    public LogSortOrder SortOrder { get; set; }
}

我有一个Web API控制器(LogsController),其中包含以下两个操作:

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

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

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

但是,我想更改第一个GET方法,所以它使用类而不是int id参数,就像这样(注意它为上面的第二个动作指定了一个不同的(单数)类型):

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

这个GetLogRequestDto类看起来像这样:

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

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

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

  

找到了与请求匹配的多项操作

在这两种方法中,它不区分用作params的两种不同类型。

我配置的默认路由是:

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

我无法弄清楚为什么会出现问题 - 为什么它以某种方式工作而不是另一种方式。

1 个答案:

答案 0 :(得分:0)

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

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

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

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

此处摘录自Mark Vincze博客文章,其中包含用于验证操作参数的操作过滤器属性:

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));
                }
            }
        }
    }
}