Multiple parameters in a web api 2 get

Nick picture Nick · Aug 6, 2014 · Viewed 54.8k times · Source

I want to make a web api that is passed 4 parameters.

Here is my route:

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{email}/{firstname}/{lastname}/{source}"
        );

Here is the method signature

public string GetId(string email, string firstname, string lastname, string source)

Here is the calling url

http://fakedomain.com/api/Contacts/[email protected]&firstname=joe&lastname=shmoe&source=123

I get a 404 error.

If I set each parameter to optional in the route config, and set up each argument with a default value it gets called. However, each argument gets the default value and not the passed value.

I feel like I am close, what am I missing?

Answer

vesuvious picture vesuvious · Aug 6, 2014

You don't need a special routing record to handle multiple parameters. The routing record you created would be looking for the following route

/api/controller/[email protected]/Dan/FunnyLastName/TheCoffeeShop

but you are trying to pass in parameters, not specify a route.

with this routing record:

config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", new { id = RouteParameter.Optional, action = "DefaultAction" });

the following GET endpoint:

public HttpResponseMessage Get(int requestId = 0, string userName = null, string departmentName = null, bool includeCompleted = false)
    {
      //code
    }

could be hit as :

 /api/controllername/?requestId=15&username=Dan

or

/api/controllername/?departmentName=SoftwareEngineering

or any other combination of the parameters (or no parameters since they have default values)

Since you have a "Named" action (GetId) instead of the default actions (GET,POST,PUT..), this complicates things a little bit and you would have to work out a custom route to handle the action name. The following is what I use for custom action names (id is required in this example)

config.Routes.MapHttpRoute("ActionRoute", "api/{controller}/{action}/{id}");

Your endpoint would have to explicitly accept one parameter with the name 'id'

  public HttpResponseMessage LockRequest(int id, bool markCompleted)
    {
        //code
    }

This endpoint would be hit at the following route:

/api/controllerName/LockRequest/id?markCompleted=true

Following the RESTful spec, it is better to stay away from custom action names when possible. Most of the time you can get away with the normal HTTP verbs and just use named actions to manipulate existing items (hence why ID is required in my example). For your code you could just have two GET endpoints, one that takes a specific ID to get the item, one that returns all items (including ids) based on "search parameters".

public HttpResponseMessage Get(int id)
public HttpResponseMessage Get(int requestId = 0, string userName = null, string departmentName = null, bool includeCompleted = false)

These would both be handled by the default routing record.