3

I just read this post by Dave Ward (http://encosia.com/using-jquery-to-post-frombody-parameters-to-web-api/), and I'm trying to throw together a simple web api controller that will accept a viewmodel, and something just isn't clicking for me.

I want my viewmodel to be an object with a couple DateTime properties:

public class DateRange
    {
        public DateTime Start { get; set; }
        public DateTime End { get; set; }
    }

Without changing anything in the stock web api project, I edit my values controller to this:

public IEnumerable<float> Get()
    {
        DateRange range = new DateRange()
        {
            Start = DateTime.Now.AddDays(-1),
            End = DateTime.Now
        };

        return Repo.Get(range);
    }

    // GET api/values/5
    public IEnumerable<float> Get(DateRange id)
    {
        return Repo.Get(range);
    }

However, when I try to use this controller, I get this error:

Multiple actions were found that match the request: System.Collections.Generic.IEnumerable1[System.Single] Get() on type FEPIWebService.Controllers.ValuesController System.Collections.Generic.IEnumerable1[System.Single] Get(FEPIWebService.Models.DateRange) on type FEPIWebService.Controllers.ValuesController

This message appears when I hit

/api/values

or

/api/values?start=01/01/2013&end=02/02/2013

How can I solve the ambiguity between the first and second get actions?

For further credit, if I had this action

public void Post(DateRange value)
{
}

how could I post the Start and End properties to that object using jQuery so that modelbinding would build up the DateRange parameter?

Thanks!

Chris

4

1 回答 1

2

The answer is in detail described here: Routing and Action Selection. The Extract

With that background, here is the action selection algorithm.

  1. Create a list of all actions on the controller that match the HTTP request method.
  2. If the route dictionary has an "action" entry, remove actions whose name does not match this value.
  3. Try to match action parameters to the URI, as follows:
    • For each action, get a list of the parameters that are a simple type, where the binding gets the parameter from the URI. Exclude optional parameters.
    • From this list, try to find a match for each parameter name, either in the route dictionary or in the URI query string. Matches are case insensitive and do not depend on the parameter order.
    • Select an action where every parameter in the list has a match in the URI.
    • If more that one action meets these criteria, pick the one with the most parameter matches.

4.Ignore actions with the [NonAction] attribute.

Other words, The ID parameter you are using, is not SimpleType, so it does not help to decide which of your Get methods to use. Usually the Id is integer or guid..., then both methods could live side by side

If both of them would return IList<float>, solution could be to omit one of them:

public IEnumerable<float> Get([FromUri]DateRange id)
{
    range = range ?? new DateRange()
    {
       Start = DateTime.Now.AddDays(-1),
       End = DateTime.Now
    };
    return Repo.Get(range);
}

And now both will work

/api/values

or

/api/values?Start=2011-01-01&End=2014-01-01
于 2013-06-01T15:46:04.187 回答