简体   繁体   中英

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

I have the following class:

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

    public LogSortOrder SortOrder { get; set; }
}

I have a Web API Controller (LogsController) with the following 2 actions:

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

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

The first for retrieving a specific log, and the second for retrieving a list of logs. When I make a GET request for a specific log via: /logs/123, it calls the 1st action correctly, and likewise if I make a GET request for /logs it calls the 2nd action correctly (the properties defined in that class are optional and don't need to always be provided).

However, I wanted to change the first GET method so it uses a class instead of the int id parameter, like this (note it's specifying a different (singular) type to the 2nd action above):

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

This GetLogRequestDto class looks like this:

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

My reasoning behind this approach was so that I can have validation of the model go through my standard ModelStateValidationActionFilter , and also put any specific validation attributes inside this class, rather than when using the 'int id' parameter approach, then having to perform validation.

When I implement this approach though and attempt to call /logs/1, I get the following error:

Multiple actions were found that match the request

It's not differentiating between the 2 different types used as params in these 2 methods.

The default route I have configured is:

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

I can't figure out why there is a problem - why it works one way but not the other.

Using a complex type for handling a single basic type parameter (that is also part of the route) in GET requests is not a great idea.

By using this approach the framework will not be able to bind your route parameter to that complex type (the route definition requires an id parameter that must be a simple type).

I strongly suggest you to revert your changes and make the id parameter again an int .

As an alternative approach you may follow this great post and implement an action filter that may validate your method parameters decorated by validation attributes even if they are simple types.

Here it is an excerpt from Mark Vincze's blog post representing the action filter attribute used to validate action parameters:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM