I am using web api 2 to implement a restful service. After doing some research on best practices everyone seems like they are having different opinions on how to do the following. I have a GET
public HttpResponseMessage Get(string crewId, string shiftDate, int offset = 1, int limit = 10)
This GET method returns a list. There are multiple ways of getting the data from this method.
Get by crewId only
Get by shiftDate only
or
Get by crewId and shiftDate
Do you (1) Mark the crewId and shiftDate as optional?
public HttpResponseMessage Get(string crewId = null, string shiftDate = null, int offset = 1, int limit = 10)
and then have bunch of if statements to check what is filled and what is not filled to be able to do actions
if(crewId != null && shiftDate == null){
// Get by crewId
}else if(crewId == null && shiftDate != null){
// Get By shiftDate
}else if(crewId != null && shiftDate != null){
// Get By crewId and shiftDate
}
To me this looks crazy to do especially if you have many parameters you would have too many "if" statements in your code.
Do you (2) Have different set of gets?
public HttpResponseMessage GetByCrewId(string crewId, int offset = 1, int limit = 10)
public HttpResponseMessage GetByShiftDate(string shiftDate, int offset = 1, int limit = 10)
public HttpResponseMessage GetByCrewIdShiftDate(string crewId, string shiftDate, int offset = 1, int limit = 10)
and then you would have your URI Route map to the method
.../api/GetByCrewId?crewId=1234
.../api/GetByShiftDate?shiftDate=1111-11-11
.../api/GetByCrewIdShiftDate?crewId=1234&shiftDate=1111-11-11
is option 2 restful?
Or is there an better options (3).
Both options above will work just making sure I am using best practices and following REST standards. Just seems like I am missing something and hopefully you can put me in the right direction.
You don't want option (2) - to be RESTful you want nouns in your URL.
So you want your URL's to look like:
/api/items/?crewId=1234&shiftDate=1111-11-11
(I can't work out what your Items are, based on the 'Crew' and 'Shift' parameter names. What has a Crew and a Shift date?? Fishing trips?? If so, the url would be better as /api/fishing-trips/?crewId=....&shiftDate=...
As for the controller, I'd go for something like:
public HttpResponseMessage Get(string crewId = null, string shiftDate = null, int offset = 1, int limit = 10) {
return dataSource.Where(x=> (x.CrewId == crewId || crewId == null)
&& (x.ShiftDate == shiftDate || shiftDate == null));
}
Reviewing Best Practices: Understanding REST Headers and Parameters it states that the use of the query parameter would indicate that it is optional. If you can make a single value required and the others optional it might help clarify the URI.
/api/fishing-trips/crew/{crewid}?shiftdate=1111-11-11
In the end if your items are all optional then use of the "?" is probably the best route. Further information on types of parameters are available at RFC 6570.
Note that your choice may have impact on any queueing that you choose to use and the path-style parameter expansion may make the most sense. More information also at Understanding REST Parameters.
Lastly you may wish to create these as search parameters then, if you find that your users are often requesting the same search, you can package it into a single REST path.
For example,
/api/fishing-trips/search?crew=1234
/api/fishing-trips/search?shiftDate=1111-11-11
/api/fishing-trips/search?crew=1234&shiftDate=1111-11-11
You could also provide a simplification along with optional parameters, for example,
/api/fishing-trips/today
/api/fishing-trips/today?crew=1234
/api/fishing-trips/crew/1234/today
These last examples are subjective from my research but more information is available at Best Practices for a Pragmatic Rest API and RESTful URL design for search.
I've done something like this before. Since you can use one or the other or both, I would use the optional parameters:
public HttpResponseMessage Get(string crewId = null, string shiftDate = null, int offset = 1, int limit = 10)
Then construct your query. For example, something like this:
var query = "";
if (!String.IsNullOrEmpty(crewId)) {
query += $"crewId='{crewId}'";
}
if (!String.IsNullOrEmpty(shiftDate)) {
if (query.Length > 0) query += " AND ";
query += $"shiftDate='{shiftDate}'";
}
if (query.Length == 0) {
//neither crewId or shiftDate were given
//return some kind of error
}
Considering that I am about to implement this myself, I will say it should be a single GET
method action with multiple optional parameters.
Why? Because you should not worry about this querying logic in the REST API layer. After all, you are effectively creating an AND
discrimination clause with multiple parameters (i.e. CrewId = 1 AND ShiftDate = 2016-01-01
). And if you don't supply parameters, return all the items.
I will be passing my parameters all the way through to a SQL stored procedure which has default values specified and will return the results based on the parameters passed.
Remember that in many ways, REST methods map directly to CRUD, so treat your API as such: REST API Tutorial