Avoid magic string in ASP.NET Core attribute routing - asp.net-core

Consider this example:
[HttpGet]
[Route("[action]/{id}")
public IActionResult Foo([FromRoute] int id) {
//etc.
}
I can avoid magic strings for areas, controllers, actions by using the special tokens [controller], [action], etc.
This is a simple action with a single int argument. Is it also possible somehow to avoid magic string {id}?

Related

asp.net core api - how to distinguish between `api/cars` and `api/cars?color=red` calls with [FromQuery] object

I am using [FromQuery] atribute in controller's Get method:
//CarsController, etc..
[HttpGet]
public async Task<ActionResult<IEnumerable<CarsDto>>> Get([FromQuery] CarsParameter? carsParam = null)
{
//param is always not null here
}
Inside the method I need to distinguish between api/cars and api/cars?color=red calls. Problem is, that carsParam object is never null, so I cannot say if the Color="" (defailt value) is intended to be empty string or it's because of the call was api/cars
CarsParameter is a simple class:
public class CarsParameter
{
public string Color {get; set;} = "";
//more params here
}
Yes, I can use different path, like api/cars/withParams?color=red, but i am looking for more subtle solution.
I need to distinguish between api/cars and api/cars?color=red calls. Problem is, that carsParam object is never null
Please note that default model binding starts by looking through the sources for the key carsParam.Color. If that isn't found, it looks for Color without a prefix, which cause the issue.
To achieve your requirement, you can try to specify prefix explicitly, like below.
public async Task<ActionResult<IEnumerable<CarsDto>>> Get([FromQuery][Bind(Prefix = "carsParam")] CarsParameter? carsParam = null)
{
Request to api/cars?color=red&carsParam.color=yellow&carsParam.brand=test and following is test result

ASP.NET Core WebAPI 3.1 multiple parameters vs Complex object httpget

Not done API for a while and wanted to make sure what is the best practice to make a call when you need to pass multiple parameters in an HttpGet
Option 1
[HttpGet("getpet", Name = nameof(GetPet))]
[ProducesResponseType(typeof(PetResponse), (int)HttpStatusCode.OK)]
public async Task<ActionResult<<PetResponse>> GetById(
[FromQuery]int id,
[FromQuery]bool dogsOnly)
Option 2
use a Complex Object.
[HttpGet("getpet", Name = nameof(GetPet))]
[ProducesResponseType(typeof(PetResponse), (int)HttpStatusCode.OK)]
public async Task<ActionResult<<PetResponse>> GetById([FromQuery]PetRequest request)
public class PetRequest
{
public int Id { get; set; }
public bool DogsOnly { get; set; }
}
Any suggestions or limitation of any of the approach eg test in postman?
Any suggestions or limitation of any of the approach eg test in
postman?
The two option use the same way to test on Postman.
If your query strings would not change,the two options are all acceptable.But if you need to change the query string afterwards and the same query strings appear many times in your application,creating a PetRequest model is much better.

.Net Core API Endpoint not allowing QueryString parameters

Very possible this is a duplicate, but I've looked and can't find an answer. The first answer here looked promising: Query string not working while using attribute routing But I tried that and it didn't work.
[HttpGet, Route("api/machine/byid/{id=id}/{pageNumber=pageNumber}/{pageSize=pageSize}/{fields=fields}")]
public string ById(int id, int pageNumber, int pageSize, string fields)
// code removed
}
This works:
https://localhost:44303/api/machine/byid/1/2/3/a,b,c
This does not:
https://localhost:44303/api/machine/byid?id=1&pageNumber=2&pageSize=3&fields=a,b,c
The second url returns:
{"type":"https://www.rfc-editor.org/rfc/rfc7231#section-6.5.1","title":"One or more validation errors occurred.","status":400,"traceId":"|bf12950b-472923d3a24062d1.","errors":{"id":["The value 'id' is not valid."],"pageSize":["The value 'pageSize' is not valid."],"pageNumber":["The value 'pageNumber' is not valid."]}}
You would need two routes:
[HttpGet("api/machine/byid")]
public string ById(
[FromQuery("id")] int id,
[FromQuery("pageNumber")] int pageNumber,
[FromQuery("pageSize")] int pageSize,
[FromQuery("fields")] string fields)
{
}
Follow this link for more informations
The example you provided demonstrates route parameters. There is a distinct difference between route parameters and query parameters.
To accomplish query parameters, you can the [FromQuery] attribute to your method parameters. This will allow for the query parameter example that you provided,
Example : https://localhost:5000/api/persons?firstName=bob&lastName=smith
You can also provide default values for these from within your method parameters. You can string multiple query parameters together in one action.
For route parameters, the parameters are provided via the route itself.
Example : https://localhost:5000/api/persons/23
These parameters are defined from within the [HttpGet("{id}")] attribute on your controller action. You can also constrain the parameter to a certain type, such as an int. This is achieved by adding a colon and specifying the type. Example [HttpGet("{id:int}")]. No further attributes are required to be added within your method parameters for route parameters.
Of course you must also declare these parameters in your method parameters, for both types.
// "/api/persons/23"
[HttpGet("{id}")]
public async Task<IActionResult> GetPersonById(int id)
{
// Code ...
}
// "/api/persons?firstName=bob&lastName=smith"
[HttpGet]
public async Task<IActionResult> GetPersonByName([FromQuery] string firstName = null, [FromQuery] string lastName = null)
{
// Code here... both firstName and lastName can now be optional or only one provided
}
The answer by sturcotte06 was close, but was not 100% Core compliant. This works:
[HttpGet, Route("api/machine/byid/{id=id}/{pageNumber=pageNumber}/{pageSize=pageSize}/{fields=fields}")]
public string ById([FromQuery] int id, [FromQuery] int pageNumber, [FromQuery] int pageSize, [FromQuery] string fields)
{
// code removed
}

how we can design .net core api controller to accept an array value of strings

I would like to know how we can design .net core api controller to accept an array value like given below
http://localhost:32656/api/Values?str[]="abc"&str[]="xyz"
I did some research online and the only two options, I was able to find is either I need to pass indexes inside the array
eg:- http://localhost:32656/api/Values?str[0]="abc"&str[1]="xyz" (Pass indexes inside the array)
or I need to pass the array as repeated query strings.
eg:- http://localhost:32656/api/Values?str="abc"&str="xyz" (Pass it as repeated query strings)
But I would like to see the other possible options to send an array to .net core 2.1 api controller.
You can take advantage of the FromQuery attribute here, specifying the Name property as str[]:
public IActionResult Values([FromQuery(Name = "str[]")] List<string> strValues)
{
...
}
If you also want to strip out the "s for each value, you can use a Select. Here's a an example:
public IActionResult Values([FromQuery(Name = "str[]")] List<string> strValues)
{
var strValuesWithoutQuotes = strValues.Select(x => x.Trim('"'));
...
}
Here is how we do it:
[Route("api/v1/[controller]")]
public class TestController : Controller
{
[HttpGet]
public async Task Get(List<string> stringValues)
{
...
}
}
Then call the endpoint with http://localhost/api/v1/test?stringValues=string1&stringValues=string2
stringValues should have the list of values in the query string

Provide common data to all ASP.NET MVC 4 controller method

I am rewriting an ASP.NET webforms app in MVC4 and was wondering how to solve the following problem. It is a multi-tenant app, so part of the URL has the tenant NAME in it:
http://mysite/tenant/controller/action
But tenant is an abbreviation representing the tenant, but I'd like to always convert that to the corresponding integer id and use that throughout the code. What is the best way to write that convert code once and have some variable/property available to all controller methods.
public class DivisionController : Controller
{
//
// GET: /Division/
public ActionResult Index()
{
// I want this.TenantId to be available in all controller methods
FetchDivisions(this.TenantId);
return View();
}
Is a base controller the best way to handle this or filters or attributes?
Yes a base controller will handle this just fine. If you need to perform a database lookup to convert the abbreviation to the integer value you can use the OnActionExecuting event like so:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
// Lookup code here.
}