Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Searching with WebAPI

I have made a web API class for my Customer model. I have the standard methods (GET, POST, PUT, DELETE). The problem is, I want to implement another GET method which is a search. Something like this:

[HttpGet]
public IEnumerable<Customer> Search(string id)
{
    var customers = customerRepository.Search(id);
    return customers;
}

The search method performs a search based on the account number of my customers, using the .Contains() method.

The problem is, when I navigate to: mySite.com/api/Customers/Search/123 I get a 404. What am I doing wrong here?

like image 729
CallumVass Avatar asked Jul 25 '12 10:07

CallumVass


2 Answers

While Darin's answers are always of top quality this question would actually benefit from an answer that explains how searching, paging and filtering should actually be done in any API and how it should be done using the most current version of Web API (v2).

This is a post which I consider a good resource on the matter (technology indenpendent): http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api

The answer should also reflect what's new in ASP.NET Web API v2 because Darin's answer is quite old.

Since this question comes up at the top when doing Google search for "asp.net web api searching" I will try to explain few things here.

To get as close as possible to REST principles with the latest version of ASP.NET Web API (v2) one should take a serious look at attribute routing that was introduced in the latest version. It is very hard to achieve RESTful routing with the old, classic, convention based routing (in global.asax.cs or RouteConfig.cs).

You should read more about that here http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

Now, to go into details how to implement the specifics you ask about.

The most common practice is to expose these types of functionality through query string parameters.

Per REST principles, you should have one endpoint for your Customers resource, for instance

/api/customers

To achieve this you would decorate your GetCustomers() action in your Web API controller like this

[HttpGet]
[Route("/api/customers")]
public HttpResponseMessage GetCustomers(string q="", string sortBy="", string sortDirection="", bool active=true, ...)
{ 
// q = being optional search query
// sortBy = optional sort by column/property
// sortDirection = optional sort direction
// active = filter on 'active' column/property
// ... other filters may be applicable
}

You would implement this action closely to what you did in classic MVC if you wanted to provide filtered Views.

I would only introduce new controllers and custom actions if really needed, for some custom edge cases.

with regards to a comment about SearchFilter strongly typed object, let's explain that this won't work out of the box because the default model binder will not bind to this class when using GET requests.

So I'd either take those properties out of SearchFilter class and put them on the action itself so they'd bind via query string binder or use the [FromBody] binder if you wanted to bind from the request body. As per http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api

HTH

like image 103
mare Avatar answered Oct 13 '22 18:10

mare


As per the default route setup only the standard controller action names are allowed (the RESTful ones and the dispatching is done based on the HTTP verb). If you want to violate the RESTful conventions and use some custom action names then you will have to modify your route setup in order to include the action name in the url: api/{controller}/{action}/{id}. Now you can send a request to /api/Customers/Search/123 which will invoke the Search action on the Customers API controller.

like image 22
Darin Dimitrov Avatar answered Oct 13 '22 19:10

Darin Dimitrov