Optional query string parameters in ASP.NET Web API - c#

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?

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.

Use initial default values for all parameters like below
public string GetFindBooks(string author="", string title="", string isbn="", string somethingelse="", DateTime? date= null)
{
// ...
}

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} _
)

Related

Calling Get method with Object Parameter .NET core

I am developing a dashboard in react which calls backend API to fetch all recipes from the database. So the search criteria would be huge. Its required to pass many filter attributes into the backend to get the correct recipes.
As an example below I have defined a class for Search Parameters
public class SearchParams
{
public string TemplateName { get; set; } = "";
public DateTime DateFrom { get; set; }
public DateTime DateTo { get; set; }
public String CreatedBy { get; set; } = "";
public Guid Id { get; set; }
}
So the GET method is required to handle whatever the parameters provided fetch the corresponding recipes from the DB accordingly.
But since GET requests doesnt support accepting parameters as OBJECT (SOrry if I am wrong) I thought about trying with POST. But that feels a little confused to use POST for a search functionality.
So with GET method do I need to define with all the parameters like this
[HttpGet]
public IEnumerable<Recipes> Get(string TemplateName,DateTime DateFrom....)
{
return new string[] { "value1", "value2" };
}
Or any best approach for this?
Please note, my real search criteria include many attributes other than the properties in my class definition above.
nothing prevents you from using SearchParams as an input parameters
[HttpGet]
public IEnumerable<Recipes> Search(SearchParams par)
the only problem is that Get doesn't include a body, so all data should be included in a query string
.../search?TemplateName=TemplateName&CreatedBy=....

Bind parameters from query and form at the same time in ASP.NET

I'm working on an ASP.NET Web API with the following class parameter example:
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
public class DetailsParameters
{
[SwaggerParameter("NameParameterDescription")]
[FromQuery(Name = "name")]
public string Name { get; set; }
[SwaggerParameter("DescriptionParameterDescription")]
[FromQuery(Name = "description")]
public string Description { get; set; }
// And many more . . .
}
Then I use it in my controller:
public class DetailsController : ControllerBase
{
[HttpGet]
public IActionResult GetDetails([FromQuery] DetailsParameters detailsParameters)
{
// Some code here, work with the params inside detailsParameters variable
}
}
But now I want to add a POST method that should use the exact same thing - same parameters with same swagger descriptions. The difference I need is those parameters to be taken as form parameters, not as query params.
The method in the controller should look like this:
public IActionResult AddDetails([FromForm] DetailsParameters detailsParameters)
{
// do something with those parameters
}
But they are still query parameters in the parameters object. I tried to modify the properties and added FromForm attribute:
[SwaggerParameter("NameParameterDescription")]
[FromQuery(Name = "name")]
[FromForm(Name = "name")]
public string Name { get; set; }
Unfortunately, it does not work as I wanted. A workaround would be to create a new class with all the same properties and description but with different binding attributes.
Still, I believe there should be a better way to resolve this. Any ideas?

net core API Object Array Parameter always null

I have this class
public enum Comparison
{
Eq,
Neq
}
public class Filter
{
public string Name { get; set; }
public string Value { get; set; }
public Comparison Comparison { get; set; }
}
My Action should accept an Array of Filters
public async Task<IActionResult> Index([FromQuery(Name = "filter")]Filter[] filter)
{
return Ok();
}
filter is always an empty array with this url
https://localhost:5001/configtemplates?filter=%7B%0A%20%20%22Name%22%3A%20%22Name%22%2C%0A%20%20%22Value%22%3A%20%22Test%22%2C%0A%20%20%22Comparison%22%3A%201%0A%7D&filter=%7B%0A%20%20%22name%22%3A%20%22Description%22%2C%0A%20%20%22value%22%3A%20%22Test%22%2C%0A%20%20%22comparison%22%3A%201%0A%7D.
When change the type of filter to string, it maps a string[]. therefore i expect the issue to be in the binding of Filter[].
How can i make it work bind to Filter[]?
UPDATE:
Filter[] can be parsed if the URL is in this format https://localhost:5001/configtemplates?filter%5B0%5D.n=Name&filter%5B0%5D.c=6&filter%5B0%5D.v=bla&filter%5B1%5D.n=Name&filter%5B1%5D.c=6&filter%5B1%5D.v=bli&filter%5B2%5D.n=ID&filter%5B2%5D.c=6&filter%5B2%5D.v=blu
which is OK for now but i am not sure if this is official standard. e.g. swagger does not work.
Thank you!
You can use System.Text.Json to Deserialize it.
Example link: https://localhost:5001/api/test/Index?filter=[{%22name%22:%22HalloWelt%22},{%22Name%22:%22bbbb%22}]
public async Task<IActionResult> Index([FromQuery(Name = "filter")]string filter)
{
List<Filter> filters = System.Text.Json.JsonSerializer.Deserialize<List<Filter>>(filter);
return Ok();
}

How to split a string and pass it into your get;set method

I have a web api that accepts a string. The string looks as follows
14523:GFTRED3545EDH
Now it gets passed to my command page:
public class CaptureCommand : Command
{
// how do i split my string in here so that it goes to the correct parameter
public int id { get; set; } //pass in 14523
public string code { get; set; } //pass in GFTRED3545EDH
}
I'm not sure how to split my string up so that ID gets 14523 and code gets GFTRED3545EDH
Your assistance would be appreciated.
You can use String.Split. If you're passing it in via the constructor then it'll be like this:
public CaptureCommand(string value)
{
var parts = value.Split(':');
id = int.Parse(parts[0]);
code = parts[1];
}

Is there a better way to handle default query params using [FromUri]?

I have the following endpoint which has two query parameters (both optional):
[HttpGet]
[Route("")]
[ResponseType(typeof(List<ResourceResponse>))]
public IHttpActionResult GetResources([FromUri]ResourceRequest request = null)
{
request = request ?? new ResourceRequest();
var resources = ResourceService.GetResources(request.SiteId, request.ServiceId);
return Ok(resources);
}
And here is my request object:
public class ResourceRequest
{
[DefaultValue(null)]
public int? ClubId { get; set; }
[DefaultValue(null)]
[FromClubId(nameof(ClubId))]
public int? SiteId { get; private set; }
[DefaultValue(null)]
public Guid? ServiceId { get; set; }
}
This code runs fine, but I need to include request = request ?? new ResourceRequest(); as the first line to account for the case where there are no query parameters submitted.
Is there a better way to handle the no-query-parameters scenario? Or is this as good as it gets?
I need to declare my query params as the ResourceRequest class so that I can run Validation Attributes on them.
I can't use new ResourceRequest() as my default either because it is not a compile-time constant.
You could just use safe navigation operator (introduced in c# 6) like this:
public IHttpActionResult GetResources([FromUri]ResourceRequest request = null)
{
var resources = ResourceService.GetResources(request?.SiteId, request?.ServiceId);
return Ok(resources);
}
The GetResources method will received null if the request is null.

Categories