Using Other Verbs with Web API

Full source code available here.

You will often use the GET verb when making requests to an API. You have probably used it like this –

www.example.com/person/ or
www.example.com/person/1 or
www.example.com/person?firstname=dave&lastname=daniels&age=22

All very simple, but what if you wanted to to search for many people at the same time, and I don’t mean all the people aged 22, or all the people with the first name Dave.

What if you wanted to look for Dave, Tom, Steve and Dan in one request, or all the people aged 22, 33, or 44.

You could go down the road of adding them to the query string, something like

www.example.com/person?firstperson_firstname=dave&secondperson_firstname=tom&...

But this a bad idea.

This is where the HTTP SEARCH method comes in. If this doesn’t sound familiar, that might be because it is currently an IETF draft https://tools.ietf.org/html/draft-snell-search-method-00, but tools like Fiddler support it.

SEARCH differs from GET by having a body. In this body you can have any content you want. For our scenario we will use a json body with an array of things to search for. You have to specify in the header what the content type of the body, e.g. Content-Type: application/json, (just like you do with a POST or a PUT).

I’ve created a very simple POCO to represent the things I want to search by.

public class PersonSearchModel
{
    public string Firstname { get; set; }
    public string Lastname { get; set; }
    public int Age { get; set; }
}

Into the body of the SEARCH request I have a json array of PersonSearchModel.

[
  {
    "firstname": "Dave",
    "lastname": "Davis",
    "age": 22
  },
  {
    "firstname": "Larry",
    "lastname": "Landers"
  },
  {
    "firstname": "Steve",
    "lastname": "Smith",
    "age": 44
  }
]

Now we can search for as many people as we like in as complicated way as we like.

In the PersonController I have a Search method –

[AcceptVerbs("SEARCH")]
public async Task<IActionResult> Search([FromBody]List<PersonSearchModel> people)
{
	// Perform search
	// snip..
}

Inside the body of the method you can search in any manner you choose.
here.

Full source code available here.

Web API 2 Controller with multiple GET methods – part 2

I have two other posts on multiple GET methods, one for ASP.NET 5 Web Api, and another for Web Api 2.

Download full source code.

A few months ago I wrote a post explaining how to create a controller with multiple GET methods.
In that I used the combination of [RoutePrefix..] and [Route...] to generate routes like

  • http://…/api/values/geta
  • http://…/api/values/getb

Attribute parameter names and type

In this post I will show a different way of achieving multiple GETs by matching the type of parameter passed to the controller with the appropriate method.

In the first example I will match a method to an int being passed in, note the [Route("{id:int}")], this specifies that the expected parameter is named “id” and is an int.

// GET api/values/7
[Route("{id:int}")]
public string Get(int id)
{
    return $"You entered an int - {id}";
}

It is no problem to add another GET method with a [Route("{id:Guid}")]. This works fine because there is no way and int and Guid can be confused.

// GET api/values/AAC1FB7B-978B-4C39-A90D-271A031BFE5D
[Route("{id:Guid}")]
public string Get(Guid id)
{
    return $"You entered a GUID - {id}";
}

Where we start having problems is when the attribute routing system can’t determine which method you were attempting to call because it can’t distinguish an int from a long (or a string, decimal etc.)

For example, if we had another GET method taking a long, it would cause many runtime errors.

// GET api/values/8 - this will not work because it could be an int or a long
// GET api/values/4147483647 - this works because it can ONLY be a long
[Route("{id:long}")]
public string Get(long id)
{
    return $"You entered an long - {id}";
}

If we called http://...api/values/8, the attribute routing system has no way of knowing whether the GET method for the int or the long should be called.

But if we called http://...api/values/4147483647, that number is larger than an int can hold, the attribute routing system knows only one method matches the call.

If you want to have longs and ints in the same controller one of the routes needs to be altered. Note the new route – [Route("large/{id:long}")].

To call it now use http://...api/values/large/8 or http://...api/values/large/4147483647

[Route("large/{id:long}")]
public string Get(long id)
{
    return $"You entered an long - {id}";
}

Download full source code.