web api 2 - how to make api versioning more elegant - asp.net-web-api2

What i want is something like the following code:
[VersionedRoute("api/user"), 1]
public class UserV1Controller : ApiController
{
[HttpGet]
[Route("aa")]
public int GetVal()
{
return 111;
}
}
[VersionedRoute("api/user"), 2]
public class UserV2Controller : ApiController
{
[HttpGet]
[Route("aa")]
public int GetVal()
{
return 222;
}
}
The key is I want the above code works just like Routeprefix with Route, they are so handy. I konw I can remove the "Route" attribute in the above code if there is only one method of each http verb, but it's not a good idea because I think in many cases we will use more methods in one controller. Now I have to write a lot of duplicated code using versionedRoute everywhere with a version number:
public class UserV1Controller : BasePublicController
{
[HttpGet]
[VersionedRoute("api/user/aa", 1)]
public int GetVal()
{
return 111;
}
[HttpGet]
[VersionedRoute("api/user/bb", 1)]
public int GetInfo()
{
return 123;
}
}
Is there any better implementation to version web api using custom header?
Or how to upgrade the above code?
There is a very similar question but I didn't find a better solution to my question.

Related

How can I get data from API to UI?

I have API my project and I want to take data from API to UI.
API CODES
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
private IProductService _productService;
public ProductsController(IProductService productService)
{
_productService = productService;
}
[HttpGet("getall")]
//[Authorize(Roles = "Product.List")]
public IActionResult GetList()
{
var result = _productService.GetList();
if (result.Success)
{
return Ok(result.Data);
}
return BadRequest(result.Message);
}
And My UI I have HomeController. I want to use in the home getList function. But I dont' know how can I get data. Can you help me?
[Files][1]
[1]: https://i.stack.imgur.com/XksVn.png

specify which API is documented by Swagger

I'm new to swagger and have it installed and running but it's picking up far more API files than desired. I have been hunting a way to specify which API is documented.
You can put an ApiExplorerSettings attribute on a controller to remove it from Swagger:
[ApiExplorerSettings(IgnoreApi = true)]
public class TestApiController : ControllerBase
{
}
If you want to apply this on a lot of controllers based on some logic,
it can be done e.g. with an action model convention: https://github.com/juunas11/AspNetCoreHideRoutesFromSwagger/blob/983bad788755b4a81d2cce30f82bc28887b61924/HideRoutesFromSwagger/Controllers/SecondController.cs#L18-L28
public class ActionHidingConvention : IActionModelConvention
{
public void Apply(ActionModel action)
{
// Replace with any logic you want
if (action.Controller.ControllerName == "Second")
{
action.ApiExplorer.IsVisible = false;
}
}
}
The convention is added in ConfigureServices like:
services.AddControllers(o =>
{
o.Conventions.Add(new ActionHidingConvention());
});

Route to controller based on query string

Problem: We are upgrading from a legacy system, so solutions are constrained. I am trying to route to an unauthorized controller if a specific query string is present. If it is not present, the user is routed to the authorized controller. This is on ASP.Net Core 2.1.
Is it possible to set the controller to route based on query string? I've tried
[/home/[action]?query={query}] -> Leads to runtime error due to '?'
[/home/[action]/{query}] - > maps to /home/index/1 (not what I need)
Thanks for any help!
Edit: Alternatively, is it possible to have a separate controller Action that depends on the query parameter?
public IActionResult Index(){}
public IActionResult Index([FromQuery]string query){}
Routing doesn't seem to distinguish between these two.
You can use IActionConstraint and IParameterModelConvention interfaces for that. In short, create an IActionConstraint like this:
public class RequiredFromQueryActionConstraint : IActionConstraint
{
private readonly string _parameter;
public RequiredFromQueryActionConstraint(string parameter)
{
_parameter = parameter;
}
public int Order => 999;
public bool Accept(ActionConstraintContext context)
{
if (!context.RouteContext.HttpContext.Request.Query.ContainsKey(_parameter))
{
return false;
}
return true;
}
}
If a matching parameter is not found on the request's query string, then it will return false from the Accept method.
Than create RequiredFromQueryAttribute class like this:
public class RequiredFromQueryAttribute : FromQueryAttribute, IParameterModelConvention
{
public void Apply(ParameterModel parameter)
{
if (parameter.Action.Selectors != null && parameter.Action.Selectors.Any())
{
parameter.Action.Selectors.Last().ActionConstraints.Add(new RequiredFromQueryActionConstraint(parameter.BindingInfo?.BinderModelName ?? parameter.ParameterName));
}
}
}
Than you could decorate your mandatory query string parameters with this attribute:
[Route("api/[controller]")]
public class ValuesController : Controller
{
[HttpGet("{id}")]
public string Get(int id, [RequiredFromQuery]string foo, [RequiredFromQuery]string bar)
{
return id + " " + foo + " " + bar;
}
}
From now than, only the following URL GET api/values/5?foo=a&bar=b would lead into the action above, all other combinations of parameters would result in response with status 404, which you can eventually replace with what you want.
You can find more info at this link https://www.strathweb.com/2016/09/required-query-string-parameters-in-asp-net-core-mvc/

Web Api Routing : Multiple controller types were found that match the URL

I'm getting,"Multiple controller types were found that match the URL", Error while performing postman operation for the below API Calls.
Can someone help me figuring out the attribute mapping for the same.
What I think is resolver considering the "respond" as the name of the Book.
Thanks In Advance
Code Snippet :
public class BookApiController : ApiController
{
[HttpGet]
[Route("api/v1/books/{bookName}")]
public async Task<HttpResponseMessage> Get(string bookName){
/* Code Here */
}
}
public class ProcessApiController : ApiController
{
[HttpGet]
[Route("api/v1/books/respond")]
public async Task<IHttpActionResult> Respond(string values){
/* Code Here */
}
}

Passing array of integers to an ApiController in ASP.NET Core

I have a simple api-controller in a ASP.NET Core 2.1 like this:
[Route("api/[controller]")]
[ApiController]
public class SumController : ControllerBase
{
[HttpGet("CalculateSum")]
public int CalculateSum(int a, int b)
{
return a + b;
}
[HttpGet("CalculateSumOfArray")]
public int CalculateSumOfArray(int[] values)
{
return values.Sum();
}
}
The first method works with a call like this:
https://localhost:44346/api/Sum/CalculateSum?a=1&b=2
But the second:
https://localhost:44346/api/Sum/CalculateSumOfArray?values=1&values=2&values=3
just responds with:
{"":["The input was not valid."]}.
Do I need to do something special to make it possible to send an array to an ApiController?
Add the FromQuery attribute like this:
[HttpGet("CalculateSumOfArray")]
public int CalculateSumOfArray([FromQuery(Name = "values")] int[] values)
{
return values.Sum();
}