I need to implement the following WebAPI method:
/api/books?author=XXX&title=XXX&isbn=XXX&somethingelse=XXX&date=XXX
All of the query string parameters can be null. That is, the caller can specify from 0 to all of the 5 parameters.
In MVC4 beta I used to do the following:
public class BooksController : ApiController
{
// GET /api/books?author=tolk&title=lord&isbn=91&somethingelse=ABC&date=1970-01-01
public string GetFindBooks(string author, string title, string isbn, string somethingelse, DateTime? date)
{
// ...
}
}
MVC4 RC doesn't behave like this anymore. If I specify fewer than 5 parameters, it replies with a 404
saying:
No action was found on the controller 'Books' that matches the request.
What is the correct method signature to make it behave like it used to, without having to specify the optional parameter in the URL routing?
Get
so it is automatically bound with the HTTP GET
method...
string?
is not a valid type. You can't declare string
as a nullable type since it is a reference type.
This issue has been fixed in the regular release of MVC4. Now you can do:
public string GetFindBooks(string author="", string title="", string isbn="", string somethingelse="", DateTime? date= null)
{
// ...
}
and everything will work out of the box.
It's possible to pass multiple parameters as a single model as vijay suggested. This works for GET when you use the FromUri parameter attribute. This tells WebAPI to fill the model from the query parameters.
The result is a cleaner controller action with just a single parameter. For more information see: http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
public class BooksController : ApiController
{
// GET /api/books?author=tolk&title=lord&isbn=91&somethingelse=ABC&date=1970-01-01
public string GetFindBooks([FromUri]BookQuery query)
{
// ...
}
}
public class BookQuery
{
public string Author { get; set; }
public string Title { get; set; }
public string ISBN { get; set; }
public string SomethingElse { get; set; }
public DateTime? Date { get; set; }
}
It even supports multiple parameters, as long as the properties don't conflict.
// GET /api/books?author=tolk&title=lord&isbn=91&somethingelse=ABC&date=1970-01-01
public string GetFindBooks([FromUri]BookQuery query, [FromUri]Paging paging)
{
// ...
}
public class Paging
{
public string Sort { get; set; }
public int Skip { get; set; }
public int Take { get; set; }
}
Update: In order to ensure the values are optional make sure to use reference types or nullables (ex. int?) for the models properties.
int Skip
) and there is no query param for that property specified, the API Controller method will still successfully match the request and the value for Skip
will just be the default value for that type, or 0 in this case
Use initial default values for all parameters like below
public string GetFindBooks(string author="", string title="", string isbn="", string somethingelse="", DateTime? date= null)
{
// ...
}
DateTime
is not nullable. I have already tried to use DateTime?
instead, but then MVC does not map the request to the given method if I set only some of the parameters in my HTTP request.
DateTime
is not a nullable data type. Your code should not compile, as you would not be able to assign a null
value to a parameter of type DateTime
. Perhaps, you should change it to DateTime?
, or use different value for a default like DateTime.Now
.
Datetime.Now
cannot be used in default parameter initialization, I stand corrected.
if you want to pass multiple parameters then you can create model instead of passing multiple parameters.
in case you dont want to pass any parameter then you can skip as well in it, and your code will look neat and clean.
Default values cannot be supplied for parameters that are not declared 'optional
'
Function GetFindBooks(id As Integer, ByVal pid As Integer, Optional sort As String = "DESC", Optional limit As Integer = 99)
In your WebApiConfig
config.Routes.MapHttpRoute( _
name:="books", _
routeTemplate:="api/{controller}/{action}/{id}/{pid}/{sort}/{limit}", _
defaults:=New With {.id = RouteParameter.Optional, .pid = RouteParameter.Optional, .sort = UrlParameter.Optional, .limit = UrlParameter.Optional} _
)
Success story sharing
null
is considered a constant expression, and therefore a valid default value.