2

I'm trying to add a special route to the default WebApi sample:

The regular ones are

/api/values (retrieves all values)

/api/values/{id} (retrieves a specific value (by numeric id))

Now I want to add this api:

/api/values/special

The previous api (/api/values/{id}) should serve all requests with a numeric id, but /api/values/special should serve requests that call that specific url.

So far I got this for routing:

config.Routes.MapHttpRoute("SpecialValues", "api/values/special", new { controller = "values", action = "SpecialValues"  });
config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

And this for implementation:

public class ValuesController : ApiController
{
    // GET api/values
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

    // GET api/values/5
    public string Get(int id)
    {
        return "value";
    }

    // POST api/values
    public void Post([FromBody]string value)
    {
    }

    // PUT api/values/5
    public void Put(int id, [FromBody]string value)
    {
    }

    // DELETE api/values/5
    public void Delete(int id)
    {
    }

    // GET api/values/special
    public IEnumerable<string> SpecialValues()
    {
        return new string[] { "special1", "special2" };
    }
}

But it will render: The requested resource does not support http method 'GET'.

If I call /api/values/special and I add [HttpGet] to the SpecialValues method it will work but /api/values will stop working saying that there are multiple matching actions.

Mikael Dúi Bolinder
  • 2,080
  • 2
  • 19
  • 44
Fredrik
  • 45
  • 4

2 Answers2

3

The changes to WebApiConfig is not needed. Attach a Route attribute and a HttpGet attribute. You can read more about it here.

[HttpGet]
[Route("api/{controller}/special")]
public IEnumerable<string> SpecialValues()
{
    return new string[] { "special1", "special2" };
}

WebApiConfig -

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Attribute routing.
        config.MapHttpAttributeRoutes();

        // Convention-based routing.
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}
siddharth
  • 660
  • 2
  • 6
  • 18
  • This results in the following message: The request is invalid. The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.String Get(Int32)' in 'MvcApplication1.Controllers.ValuesController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter. It is still picked up by the Get method – Fredrik Mar 02 '14 at 09:22
  • Your WebApiConfig class should be as updated in my answer. Also the url that you are invoking should be like - `http://hostname:port/api/values/special` – siddharth Mar 02 '14 at 10:26
  • 1
    Works like a charm, I had missed the part with MapHttpAttributeRoutes. Thanks! – Fredrik Mar 02 '14 at 12:47
0

yes, as mentioned above, attribute based routing is the only way to go here...

this post may also interests you Overload web api action method based on parameter type

Community
  • 1
  • 1
Deepak Raj
  • 730
  • 2
  • 9
  • 26
  • This results in an error: Multiple actions were found that match the request: Get on type MvcApplication1.Controllers.ValuesController Get on type MvcApplication1.Controllers.ValuesController – Fredrik Mar 02 '14 at 09:24