erikbozic
erikbozic

Reputation: 1665

Generate URL of a versioned API action with IUrlHelper

In my ASP.NET Core project I'm using aspnet-api-versioning like so:

[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/users")]
[Authorize]
public class UserController : Controller
{

  [HttpGet, MapToApiVersion("1.0")]
  public async Task<IActionResult> GetUsers([FromQuery] string searchString, [FromQuery] bool allOrganizations = false)
  {
   ...
  }

}    

So the request is sent to: GET /api/v1/users.

When adding another version of this action I would just add the attribute MapToApiVersion("2.0") so it would be GET /api/v2/users. That's all fine.

However in a view I call this action with ajax like so:

$.ajax({
url: '@Url.Action("GetUsers", "User", new{version = 1})' + "?searchString=" + $("#user-search").val() + "&allOrganizations=true",
type: "GET",
success: function(data) {
response($.map(data,
function(obj) {
return {
        //some mapping
       };
 }));
}
});

This calls the endpoint:/User/GetUsers?version=1?searchString={searchString}&allOrganizations=true

So it appends the version as a query parameter instead of route parameter.

If I don't add the new {version = 1} it's the same just without the 'version' query parameter.

I've also tried using @Url.RouteUrl("Get_Users", new {version =1}) and naming the action with [HttpGet("Get_Users"), MapToApiVersion("1.0")] but that just appends the query parameters to the current url.

However if I change the Controllers route attribute from [Route("api/v{version:apiVersion}/users")] to [Route("api/users")] and ommit the version parameter, the ajax call works fine.

So is there any way to use IUrlHelper (or some other way) to get the correct route to a versioned action besides hardcoding the url string? Or am I doing versioning wrong in some way?

I may just be complicating here, but I still think this should work, no?

Upvotes: 4

Views: 2238

Answers (3)

Chris Martinez
Chris Martinez

Reputation: 4368

Unfortunately, IUrlHelper is not API version-aware. I would recommend naming routes you need to resolve with a versioned name (e.g. [HttpGet(Name = "GetUsersV1")]) and then use Url.Link( "GetByUsersV1", new { version = "1.0" } ).

Upvotes: 2

Vedran Mandić
Vedran Mandić

Reputation: 1346

The (got me annoyed) solution to this is to pass the exact API version value as a string value instead of an integer as you did. So in your razor snippet the IUrlHelper instance should be called like this:

url: '@Url.Action("GetUsers", "User", new { version = "1.0" })' ...

Please notice that the version is explicitly 1.0 which correctly (in terms of syntax like MAJOR.MINOR) matches the one you have specified in the attribute like [ApiVersion("1.0")].

Upvotes: 2

Mark Redman
Mark Redman

Reputation: 24515

I appreciate this answer is not .net core specific and may not answer you question, but consider creating api controllers with the version in the name of the controller

e.g. V1Controller, V2Controller so the Urls /api/v1/GetUsers, /api/v2/GetUsers this handles versioning and keeps the code discreet between versions and reduces any method name conflicts.

Upvotes: 2

Related Questions