Search code examples
c#asp.netasp.net-web-apiroutesasp.net-web-api-routing

In Web Api, how can I specify two separate routes to one controller with two get methods without using attributes?


I have an Account controller with two Get methods, one that gets all accounts and takes no input, and another that takes an Id int and returns that specific account.

The project constraints are that we can't have id as an optional parameter on the default route, but instead need get-one-account and get-all-accounts to be accessed via different routes, and we can't use attribute routing. So our route configuration currently looks like this:

config.Routes.MapHttpRoute("AccountWithId", "api/account/{id}", new { action = "Get", id = RouteParameter.Optional }
                , new { httpMethod = new HttpMethodConstraint(HttpMethod.Get), id = @"\d+" });

config.Routes.MapHttpRoute("Default", "api/{controller}");

My two Get methods on the AccountController look like this:

public IHttpActionResult Get()
        {
            IList<AccountDto> users = m_service.Get();

            return Ok(users);
        }

        public IHttpActionResult Get(int accountId)
        {
            AccountDto user = m_service.Get(accountId);

            return Ok(user);
        }

But when I call Get with an id parameter in my test via my custom route and debug as below, it's still hitting the Get method that doesn't take an id parameter and returns all.

var url = "http://test/api/account/" + accountId;

var response = await m_server.HttpClient.GetAsync(url);

Any idea why?


Solution

  • There are a number of problems I can see here.

    Your first route does not specify a default controller and I'm surprised you didn't get a 404 when sending an accountid.

    Secondly if the id parameter is optional the regex should be \d* not \d+.

    Thirdly the parameter names in your actions should match your routing parameters, so accountId should be changed to id.

    Try this for your routing:

    config.Routes.MapHttpRoute("AccountWithId",
        "api/account/{id}",
        new { controller = "Account", action = "Get", id = RouteParameter.Optional },
        new { httpMethod = new HttpMethodConstraint(HttpMethod.Get), id = @"\d*" }
    );
    
    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
    

    and then change your action signatures to:

    public IHttpActionResult Get()
    {
        ...
    }
    
    public IHttpActionResult Get(int id)
    {
        ...
    }