2

I've been able create whatever endpoints I've wanted as long as the parameters for each one is different:

public IHttpActionResult GetFightersByWeightClass(string WeightClass)
...

public IHttpActionResult GetFighterByExactName(string NameEquals)
...

But as soon as I try to create two differently named functions that share the same parameters I am unable to use both. I have two endpoints that don't require parameters, shown below:

public class FighterController : ApiController
{
    /// <summary>
    /// Gets all fighters.
    /// </summary>
    /// <returns></returns>
    [ActionName("GetAllFighters")]
    public IEnumerable<Fighter> GetAllFighters()
    {
        return allFighters;
    }

    /// <summary>
    /// Gets all fighters that are currently undefeated.
    /// </summary>
    /// <returns></returns>
    [ActionName("GetAllUndefeatedFighters")]
    public IHttpActionResult GetAllUndefeatedFighters()
    {
        var results = allFighters.FindAll(f => f.MMARecord.Losses == 0);

        if (results == null)
        {
            return NotFound();
        }

        return Ok(results);
    }
}

Both URLs return this:

{"Message":"An error has occurred.","ExceptionMessage":"Multiple actions were found that match the request: \r\nGetAllFighters on type MMAAPI.Controllers.FighterController\r\nGetAllUndefeatedFighters on type MMAAPI.Controllers.FighterController","ExceptionType":"System.InvalidOperationException","StackTrace":" at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.SelectAction(HttpControllerContext controllerContext)\r\n at System.Web.Http.Controllers.ApiControllerActionSelector.SelectAction(HttpControllerContext controllerContext)\r\n at System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"}

Not sure why this is happening they each have their own unique action and function name, so I thought they would work like this...:

http://localhost:55865/api/fighter/GetAllUndefeatedFighters -- Just shows fighters with zero losses

http://localhost:55865/api/fighter/ -- shows all fighters

...but instead neither works. If I remove one of them, they other works and vice versa. So they aren't working when they are both active. Any idea why?

1

3 Answers 3

4

Web API allows you to use Attribute routing to customize endpoint URIs. To use it, add:

config.MapHttpAttributeRoutes();

to the Register method in your WebApiConfig class. Then you can set the endpoints to whatever you want regardless of the Action name.

[Route("getallfighters"), HttpGet, ResponseType(typeof(Fighter))]
public IHttpActionResult ThisNameDoesntMatterNow()
{
    //...
}

And your URI becomes:

api/fighter/getallfighters

You can even add attribute routing to your controller:

[RoutePrefix("api/v1/fighters")]
public class FightersController : ApiController
{  
    //...
}
Sign up to request clarification or add additional context in comments.

Comments

2

A combination of the two other answers works well for me. (I've changed the names slightly from the question.)

[RoutePrefix("api/v1/fighters")]
public class FighterController : ApiController
{
    /// <summary>
    /// Gets all fighters.
    /// </summary>
    /// <returns>An enumeration of fighters.</returns>
    [Route(""), HttpGet]
    public IEnumerable<Fighter> GetAllFighters()
    {
        return allFighters;
    }

    /// <summary>
    /// Gets all fighters that are currently undefeated.
    /// </summary>
    /// <returns>An enumeration of fighters.</returns>
    [Route("undefeated"), HttpGet]
    public IEnumerable<Fighter> GetAllUndefeatedFighters()
    {
        return allFighters.FindAll(f => f.MMARecord.Losses == 0);
    }
}

As such, your endpoints would be:

GET /api/v1/fighters

GET /api/v1/fighters/undefeated

Comments

1

Use route attribute

    /// <summary>
    /// Gets all fighters.
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [System.Web.Http.Route("api/GetAllFighters")]
    public IEnumerable<Fighter> GetAllFighters()
    {
        return allFighters;
    }

    /// <summary>
    /// Gets all fighters that are currently undefeated.
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [System.Web.Http.Route("api/GetAllUndefeatedFighters")]
    public IHttpActionResult GetAllUndefeatedFighters()
    {
        var results = allFighters.FindAll(f => f.MMARecord.Losses == 0);

        if (results == null)
        {
            return NotFound();
        }

        return Ok(results);
    }

and call two method using different route

http://www.yourdomain/api/GetAllFighters
http://www.yourdomain/api/GetAllUndefeatedFighters

1 Comment

Why we can't just use the different action name but need route attribute?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.