Controller route not found if it contains a version - c#

I am having issues when trying to route to a Controller when it is included via a external project. The controller in question works fine when included directly in the project or from the external project ONLY when the version field is removed from the route. The controller is defined as below.
[ApiController]
[ApiVersion("1.0")]
[Produces("application/json")]
[Route("api/v{version:apiVersion}/devices")]
[Authorize(Roles = UserConst.PermissionGroup.Admin)]
public class DeviceManagementController : ControllerBase{...}
Changing the route to the below works correctly
[Route("api/devices")]
Changing the route to the below fails the same way as including it through {version:apiVersion}
[Route("api/v1.0/devices")]
Any ideas on what could be wrong? With any version info the server just seems to return the default Index page which is the fallback when a controller cannot be found.
Startup.cs code for adding Api versioning
services.AddApiVersioning(config =>
{
config.DefaultApiVersion =
new ApiVersion(1, 0);
config.AssumeDefaultVersionWhenUnspecified = true;
config.ReportApiVersions = true;
});

I've done some testing.
Af first I added [Route("api/devices")] to your controller attributes
[ApiController]
[ApiVersion("1.0")]
[Produces("application/json")]
[Route("api/v{version:apiVersion}/devices")]
[Route("api/devices")]
public class DeviceManagementController : ControllerBase
and found that url .../api/v1.0/devices works properly.
After this I tryed to use just .../api/devices url.
It was working properly with [ApiVersion("1.0")] attribute
It issued a wrong version error when I changed attribute to [ApiVersion("2.0")]
so everything was working as it was expected.
Summary:
I recommend you to add [Route("api/devices")] and try to use.../api/devices url as a workaround for now, till you find out something better.

Related

ASP.NET Core API version

I'm trying to create API with ASP.NET Core and their version. I installed Microsoft.AspNetCore.Mvc.Versioning. What I like to have is all the API with the version in the URL, so it is easy to understand what version of the API I use. For example /api/v1/TableAzureCategory.
For thata, in my Startup.cs I added the following lines of code:
services.AddApiVersioning(config =>
{
config.DefaultApiVersion = new ApiVersion(1, 0);
config.AssumeDefaultVersionWhenUnspecified = true;
config.ReportApiVersions = true;
config.ApiVersionReader = new UrlSegmentApiVersionReader();
});
Then, in my API controller I added some decorations
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class TableAzureCategoryController : ControllerBase
{
}
I run the application, open Swagger and this is what I see (basically the {version:apiVersion} is not replaced with the API version)
I looked around but I found only implementation like mine above. Any ideas?
Another way of achieving this would be to create a query-based versioning solution.
Let's say we have two controllers: ExampleV1Controller and ExampleV2Controller
using Microsoft.AspNetCore.Mvc;
namespace MyAPI.Controllers
{
[ApiController]
[ApiVersion("1.0")]
[Route("api/example")]
public class ExampleV1Controller : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return new OkObjectResult("Example API v1");
}
}
[ApiController]
[ApiVersion("2.0")]
[Route("api/example")]
public class ExampleV2Controller : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return new OkObjectResult("Example API v2");
}
}
}
As by your Startup.cs configuration, it will default to API version 1.0. To make a request to the V2 version, use https://localhost:5001/api/example?api-version=2.0.
I did not test this myself, but it should work.
The {version} route parameter behaves just like any other route parameter. If the route was values/{id}/subvalues you would expect a parameter named id that must be filled in.
The API Explorer extensions for API Versioning know the version associated with an API. This value is used as the default value, but OpenAPI/Swagger generators (ex: Swashbuckle) may not use the default value without a little help (refer to the end-to-end Swagger Example). If, and only if, you are versioning by URL segment, you can have the API Explorer extensions expand the route template with the default value and remove the API version parameter using the configuration:
services.AddVersionedApiExplorer(options => options.SubstituteApiVersionInUrl = true);
In the example provided, the version route parameter will be removed and the route template will be updated to "api/v1.0/TableAzureCategory", which I presume is what you want.

ASP.NET Core 3.1 : how to route to a controller in a sub folder

How can I set up a default routing if the controller is inside a subfolder? Currently, running the code shown here, I get an error
localhost page can't be found
My folder structure is set up like this:
Project Name
> API
> Controllers
> ProductsController
ProductsController
[Area("API")]
[Route("products")]
[ApiController]
public class ProductsController : ControllerBase
{
[HttpGet]
[Route("list")]
public ActionResult<List<Product>> GetProductsList()
{
var products = _context.Products.ToList();
return Ok(products);
}
}
Startup.cs
app.UseEndpoints(endpoints =>
{
endpoints.MapAreaControllerRoute(
name: "MyProducts",
areaName: "API",
pattern: "{controller=products}/{action=list}/{id?}");
});
The [Route("products")] in your code will cause the controller to be available at URL /products instead of /api/products
To fix change it to [Route("[area]/[controller]")] or [Route("api/products")].
Edit: By the way, the folder structure of your C# files in your project has no effect in runtime, since they all get compiled in a DLL. So you can layout the C# files the way you see logical without worrying about runtime effects.

AspNetCore 3.1 routing AmbiguousMatchException

I am trying to migrate my webapp from .net core 2.1 to 3.1 and in the process changed the routing to app.UseRouting() and app.UseEndpoints(default endpoint) method and removed app.UseMvc() as mentioned in the breaking changes document.
https://learn.microsoft.com/en-us/dotnet/core/compatibility/2.2-3.1#shared-framework-removed-microsoftaspnetcoreall
Post that, facing this issue.
Details
I have 3 controllers mentioned below in the code which are correctly versioned using the attributes
Example of V2 Controller
[Microsoft.AspNetCore.Mvc.ApiVersion("2.0")]
[Route("v{version:apiVersion}")]
Controllers have similar actions methods and when I try to hit any action (example: http://localhost:xxxx/v1/GetData). I get the below exception.
Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: 'The request matched multiple endpoints. Matches:
Stateless1.Controllers.V3.SAPClientV3Controller.GetSap (SampleWebApp)
Stateless1.Controllers.V2.SAPClientV2Controller.GetSap (SampleWebApp)
Stateless1.Controllers.V1.SAPClientController.GetSap
PS: I have tested by removing this action method in rest of the two controllers and the call got through to the other controller irrespective of v1 or v2 or v3 in the http://localhost:xxxx/v1/GetData URL.
The code which supports multiple api versioning is also present in the start up.
services.AddApiVersioning((o) =>
{
o.ReportApiVersions = true;
o.DefaultApiVersion = new AspNetCore.Mvc.ApiVersion(1, 0);
o.AssumeDefaultVersionWhenUnspecified = true;
});
I had the exact same issue, and I found this ticket in microsoft's github issues https://github.com/microsoft/aspnet-api-versioning/issues/574
We need to specify the [ApiController] parameter so that the API versioning kicks in. This only started to happen when I migrated from dotnet core 2.0 to 3.1
For clarity here is what works:
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class MyController : Controller
{
[MapToApiVersion("1.0")]
public async Task<IActionResult> MethodA([FromBody] int? id)
{
return Ok();
}
}
}
[ApiController]
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class MyControllerV2 : Controller
{
[MapToApiVersion("2.0")]
public async Task<IActionResult> MethodAV2([FromBody] int? id)
{
return Ok();
}
}
}
I gave V2 names for the sake of the example, but I believe the best way to version is to use it with namespaces like V2.Home.MyController

Remove controller name from URL with Attribute routing

I want to remove controller name from URL for specific Controller.
My Controller name is Product
I found some link to do this
Routing with and without controller name
MVC Routing without controller
But all the above links done in route config file. and those are affecting other controller too. I want to do it using Attribute Routing.
Can it is possible? As I want to do this for only Product controller.
I have tried to do it on action like this
[Route("Sample/{Name}")]
but it is not working.
Gabriel's answer is right, however, it can be a bit misleading since you're asking for MVC and that answer is for Web API.
In any case, what you want is to put the annotation over the class definition instead of an action method. MVC example would be like:
[RoutePrefix("SomethingOtherThanProduct")]
public class ProductController : Controller
{
public ActionResult Index()
{
...
return View();
}
}
I'm also dropping this as an answer since you may find the following article helpful: [Attribute] Routing in ASP.NET MVC 5 / WebAPI 2
Make sure you set the RoutePrefix attribute on the whole controller class, as well as using the Route attribute on the action.
[RoutePrefix("notproducts")]
public class ProductsController : ApiController
{
[Route("")]
public IEnumerable<Product> Get() { ... }
}

Using WebApi RoutePrefix attribute on a base controller

I would like all controllers which inherit from AdminBaseApiController to be prefixed with 'admin'.
This works fine of course:
[RoutePrefix("admin")]
public class ToggleController : AdminBaseApiController
{
[Route("toggle")]
public HttpResponseMessage Get()
{
}
}
However when I move the RoutePrefix("admin") out of the ToggleController and into the AdminBaseApiController (where I want it) - The route fails and I get a 404.
Am I looking at this all wrong? Thanks in advance!
Support for inheritance has been enabled in Web API 2.2 release...You can take a look at an example in the following Release Notes:
http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-22#ARI

Categories