Defining a route with multiple parameters - c#

I would like to define a route like this
https://www.example.com/api/Company/Example/Sequence/0325671?Next=10&Reference=1&Option=none...
Where ... can be 0 to N parameters that can be null or default if not used.
Can I do this?
public IHttpActionResult GetNextRange([FromUri] ParameterDto parameters)
{
}
public class ParameterDto
{
public long Next { get; set; }
public string Reference { get; set; }
public string Option { get; set; }
}

Current examples are for .NET Core - using [FromQuery] attribute. It case it is Full .NET [FromUri] should be used.
In case the parameters are in query string you should use [FromQuery] attribute (if it is .NET Core):
public IHttpActionResult GetNextRange([FromQuery] ParameterDto parameters)
{
}
If you want to map query string parameters to object then you should add this attribute for every needed property:
public class ParameterDto
{
[FromQuery]
public long Next { get; set; }
[FromQuery]
public string Reference { get; set; }
[FromQuery]
public string Option { get; set; }
}
You can also specify name of parameter:
[FromQuery(Name = "some_query_parameter_name")]
If you don't know the exact parameters (so why do you need this?) you can get all the parameters from Request.Query (.NET Core) or Request.QueryString (Full .NET) property:
var keys = Request.Query.Keys; // or Request.QueryString.AllKeys;
var parameterValue = Request.Query["parameterName"]; // or Request.QueryString["parameterName"];

Related

How to pass an array of objects through a query string and read them in asp.net Controller

I am building an API to generate Invoice Number.
I have the following InvItem class
public class InvItem
{
public string ItemCode;
public double Quantity;
public double SaleValue;
}
I have the following Controller method
[Route("api/InvoiceMaster/GetInvoiceNum")]
[HttpGet]
public IActionResult GetInvoiceNum(
string xDateTime,
string BuyerName,
double TotalBillAmount,
InvItem[] items
)
{
...
var invItems = new List<InvItem>();
invItems.AddRange(items);
...
return Ok();
}
Invoice can have one or more items. Now I want to call that method from postman (or any other application) using GET request.
I have already built that method using POST request and reading parameters from request body. But the requirement here is STRICTLY Get Request.
I have tried the following url but cannot get the value of items in controller's action method 'GetInvoiceNum'
https://localhost:44365/api/InvoiceMaster/GetInvoiceNum?xDateTime=2020-01-01 12:00:00&BuyerName=elon&TotalBillAmount=1519&items[0].ItemCode=001897&items[0].Quantity=1&items[0].SaleValue=19&items[1].ItemCode=002899&items[1].Quantity=1&items[1].SaleValue=1500
How can I pass this array of objects to api?
You have to add from [FromQuery]
public IActionResult GetInvoiceNum(
[FromQuery] string xDateTime,
[FromQuery] string BuyerName,
[FromQuery] double TotalBillAmount,
[FromQuery] InvItem[] items
)
and convert fields to properties by adding getters/setters
public class InvItem
{
public string ItemCode { get; set; }
public double Quantity { get; set; }
public double SaleValue { get; set; }
}

How to disable case sensitive for route variable in asp net core API?

I have this
[HttpPost]
[Route("client/{clientid}/employees")]
[SwaggerOperation(Tags = new[] { "Client" })]
public async Task<Unit> AddClientEmployee(AddClientEmployeeCommand request)
{
return await _mediator.Send(request);
}
public class AddClientEmployeeCommand : IRequest<Unit>
{
[FromRoute]
public Guid ClientId { get; set; }
[FromBody]
public Employee Employee { get; set; } = new Employee();
}
The {clientid} from Route won't bind to AddClientEmployeeCommand.ClientId unless I change it to {ClientId}. Is there any way to disable case-sensitive for this case?
When you try to bind a class property with FromRoute it try to find a route section based on that property name and because the clientid is not equal with ClientId it won't bind. For solve this you should specify name for property like this:
public class AddClientEmployeeCommand : IRequest<Unit>
{
[FromRoute(Name = "clientid")]
public Guid ClientId { get; set; }
[FromBody]
public Employee Employee { get; set; } = new Employee();
}
Also for preventing binding error in calling api you can specify type in the route
[Route("client/{clientid:guid}/employees")]

Dto in GET request never appears null

I have a GET endpoint in Asp.Net Core 2.2 where I want to return all records by default and if any query provided then I am returning a search query response. Here my query Dto never comes null even no query param provided. I want to keep Dto null when no query parameter provided.
My request Dto-
public sealed class SearchQueryDto
{
public SearchQueryDto()
{
Limit = 10;
Offset = 0;
}
public string Query { get; set; }
public string PhoneNumber { get; set; }
public string ClassificationId { get; set; }
public int Offset { get; set; }
public int Limit { get; set; }
}
My Endpoint-
[HttpGet]
public async Task<IActionResult> GetAllRecords([FromQuery] [CanBeNull] SearchQueryDto request)
{
if (request != null) // always not null
{
return Ok(await _service.Search(request));
}
return Ok(await _service.GetAll());
}
Here I am expecting request to null when no query parameter provided but it always appears initialized.
The docs lie:
Route data and query string values are used only for simple types.
This is simply not true. See also Bind query parameters to a model in ASP.NET Core, it just works.
This part of the docs is true:
When model binding occurs, the class is instantiated using the public default constructor.
So, the model class will always be instantiated. Unless it's bound from the body and the body is empty, corrupt or of the wrong content-type, but that's another story.
In your case, simply check whether the model doesn't have any properties set:
public sealed class SearchQueryDto
{
private const int DefaultLimit = 10;
private const int DefaultOffset = 0;
public string Query { get; set; }
public string PhoneNumber { get; set; }
public string ClassificationId { get; set; }
public int Offset { get; set; } = DefaultOffset;
public int Limit { get; set; } = DefaultLimit;
public bool IsDefault
{
get
{
return string.IsNullOrEmpty(Query)
&& string.IsNullOrEmpty(PhoneNumber)
&& string.IsNullOrEmpty(ClassificationId)
&& Offset == DefaultOffset
&& Limit == DefaultLimit;
}
}
}
And then in your controller:
if (!request.IsDefault)
{
// ...
}
you can set default null
[HttpGet]
public async Task<IActionResult> GetAllRecords([FromQuery] SearchQueryDto request = null)
Although you do not pass query string,the model would bind the default value,so you could not get null value.
In such case,you could fix it like below:
[HttpGet]
public async Task<IActionResult> GetAllRecords([FromQuery]SearchQueryDto request)
{
var query = Request.QueryString.HasValue;
if (query)
{
return Ok(await _service.Search(request));
}
return Ok(await _service.GetAll());
}

Why is [FromQuery] attribute required?

I created an api with the following action:
[HttpGet("GetStuff/{Name}")]
public ActionResult<string> GetStuff([FromRoute]GetStuffModel requestModel)
{
if (requestModel == null) return BadRequest();
var result = doStuff();
return Ok(result);
}
The model looks like this:
public class GetStuffModel
{
public string Name { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public double MyNumber { get; set; }
}
Now I am using swagger to test this and basically the above doesn't work as I think it should. What happens is when I submit a get request with swagger and look at the values in the model, I find that only Name is captured. Latitude and Longitude have a value of 0. However, if I change the model to this:
public class GetStuffModel
{
public string Name { get; set; }
[FromQuery]
public double Latitude { get; set; }
[FromQuery]
public double Longitude { get; set; }
public double MyNumber { get; set; }
}
Then everything gets captured. My question is why do I have to specify [FromQuery] when I have already declared [FromRoute] in the controller?
Edit: I also added MyNumber variable and that one also picks up without the need of [FromQuery]
For [FromRoute] attribute model binder tries to bind values from request route data. With the current setup route data contains only 1 value for Name (well, there also action and controller values there, but it doesn't matter now) so it's possible to bind only Name property of GetStuffModel (if you add string Action property to model you'll see it will be binded as well). When you add [FromQuery] attribute to model properties it overrides model binding behavior and allows to bind certain properties from query string. In this case swagger adds Latitude and Longitude as query parameters and binding works fine.

c# WebApi OData complex type is always null in post ODataRoute

My problem is that "siteSetup" is always null for the following odata action:
[HttpPost]
[ODataRoute("Setup")]
public IHttpActionResult Setup(SiteSetup siteSetup)
{
return BadRequest("Not yet working");
}
This is my complex type
public class SiteSetup
{
public SiteSetup()
{
}
public string Username
{
get;
set;
}
public string Password
{
get;
set;
}
public string CompanyName
{
get;
set;
}
}
And this is the fiddler for a request.
Action with complextype as parameter is support in OData/WebApi V4, you may use ODataActionParameters in your controller method, you can see this page for instruction, http://odata.github.io/WebApi/#04-07-action-parameter-support

Categories

Resources