Michael Bautista
Michael Bautista

Reputation: 1220

How can I call different GET Methods in the same Controller utilizing different parameters?

I have a Controller that contains two different Get methods. One takes an int value, while the other takes a String value, like below:

public class AccountController : ApiController
{
    public String GetInfoByString(String sUserInput)
    {
        return "GetInfoByString received: " + sUserInput;
    }

    public String GetInfoByInt(int iUserInput)
    {
        return "GetInfoByInt received: " + iUserInput;
    }
}

My RouteConfig.cs is untouched, and is as follows:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

My WebApiConfig.cs is also untouched, and is as follows:

public static void Register(HttpConfiguration config)
{
    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
}

I'd like to be able to hit the Web API by using a single link like:

// Hit GetInfoByInt
http://localhost:XXXX/api/account/1

// Hit GetInfoByString
http://localhost:XXXX/api/account/abc

I can hit the first case just fine, but whenever I try to hit the second case, I get the following error:

<Error>
    <Message>The request is invalid.</Message>
        <MessageDetail>
            The parameters dictionary contains a null entry for parameter 'id' of 
            non-nullable type 'System.Int64' for method  'TestProject.String GetInfoByInt(Int64)' in 'TestProject.Controllers.AccountController'. 
            An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
        </MessageDetail>
 </Error>

Is there any way to hit either Get method depending on whether the user provided a String or an int? I'm assuming that there'll need to be changes done in either the RouteConfig or WebApiConfig, but I'm not too sure how to approach this. Maybe it's just an easy solution after all?

Also, if it matters, I'd like to be able to hit GetInfoByString with a String that contains both Letters and Numbers like 'ABC123' or '123ABC'. GetInfoByInt can be something like '123'

I'd really like to stick to one Controller rather than splitting this up into multiple Controllers if possible.

Thanks in advance for any help.

Upvotes: 4

Views: 2493

Answers (4)

Tenderdude
Tenderdude

Reputation: 321

A work around is to use nullable parameters or trying to parse the string to an integer then qualifying.

public ActionResult GetInfo(string sUserInput)
{
    var result = sUserInput.IsInt
        ? GetInfoByInt(int.Parse(sUserInput))
        : GetInfoByString(sUserInput)
    return result;
}

Upvotes: 0

Ammar Hasan
Ammar Hasan

Reputation: 2516

The following code works for me, note the ActionName and HttpGet attributes, also not in config that you need to define action parameter for api.

public class AccountController : ApiController
{
    [HttpGet]
    [ActionName("GetInfoByString")]
    public String GetInfoByString(String sUserInput)
    {
        return "GetInfoByString received: " + sUserInput;
    }

    [HttpGet]
    [ActionName("GetInfoByInt")]
    public String GetInfoByInt(int iUserInput)
    {
        return "GetInfoByInt received: " + iUserInput;
    }
}

Do the same in Config for Api

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapHttpRoute("DrawWebService",
                "api/{controller}/{action}/{value}",
                new { value = System.Web.Http.RouteParameter.Optional });

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

Upvotes: 1

Youssef Moussaoui
Youssef Moussaoui

Reputation: 12395

Here's one way of doing things if you want to keep the two actions separate:

config.Routes.MapHttpRoute("GetAccountInfoByInt", "api/account/{iUserInput}", new { controller = "account", action = "GetInfoByInt" }, new { iUserInput = @"\d+" });
config.Routes.MapHttpRoute("GetAccountInfoByString", "api/account/{sUserInput}", new { controller = "account", action = "GetInfoByString" });

Note that we're planning to make this kind of thing easier in the future by introducing attribute routing in the next version of Web API:

https://aspnetwebstack.codeplex.com/wikipage?title=Attribute%20routing%20in%20Web%20API

It should already be available if you're willing to use our nightly builds. Then all you'd have to do would be to add a couple attributes:

[HttpGet("api/account/{sUserInput}")]
public String GetInfoByString(String sUserInput)

[HttpGet("api/account/{iUserInput:int}")]
public String GetInfoByInt(int iUserInput)

Upvotes: 4

George Johnston
George Johnston

Reputation: 32258

One way of overcoming this limitation would be to use nullable parameters, e.g.

public ActionResult GetInfo(string sUserInput, int? iUserInput)

...and qualifying your logic off the non-null value.

Upvotes: 0

Related Questions