Issue with API Versioning .NET Core 2.2 UnsupportedApiVersion - api

I am creating an API and need to have versioning done in it. I am using package Microsoft.AspNetCore.Mvc.Versioning 3.1.3
My StartUp.cs is as follows
In ConfigureServices
services.AddApiVersioning(o => {
o.ReportApiVersions = true;
o.AssumeDefaultVersionWhenUnspecified = true;
o.DefaultApiVersion = new ApiVersion(1, 0);
});
services.AddMvc(options => options.EnableEndpointRouting = false).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
My 2 controllers on which I want to have versioning done are as below
namespace JWTWithRefresh.Areas.V1.CMS
{
[AllowAnonymous]
[ApiVersion("1.0")]
[Route("api/[controller]/[action]")]
public class AppController : Controller
{
public IActionResult GetApp()
{
return Ok("This is from API V1");
}
}
}
and another controller is as below
namespace JWTWithRefresh.Areas.V2.CMS
{
[AllowAnonymous]
[ApiVersion("2.0")]
[Route("api/[controller]/[action]")]
public class AppController : Controller
{
public IActionResult GetApp()
{
return Ok("This is from API V2");
}
}
}
The response I get when I make a call is as below
Endpoint = https://localhost:5001/api/App/GetApp?api-version=1.0
Response =
{
"error": {
"code": "UnsupportedApiVersion",
"message": "The HTTP resource that matches the request URI 'https://localhost:5001/api/App/GetApp' is not supported.",
"innerError": null
}
}
Please guide me in fixing this issue if anyone has gone through the same.
Thanks

For anyone else having the problem, I solved it by following suggestion from LGSon in comments above:
Solution 1:
Add [ApiController] in Controller
Solution 2:
Disable API Behavior
services.AddApiVersioning( options => options.UseApiBehavior = false );

Change Route attribute like this
[Route("v{version:apiVersion}/[controller]/[action]")]
and endpoint = https://localhost:5001/api/App/v1/GetApp or https://localhost:5001/api/App/v2/GetApp.
for more: https://www.hanselman.com/blog/ASPNETCoreRESTfulWebAPIVersioningMadeEasy.aspx

The issue can come if you dont change the version from the dropdown in the swagger ui and in the api call, you change the version number and execute.
Make sure you change the version number from the dropdown swagger api version, before giving the api version number in the api call.
version in api call

Related

OnAuthorizationAsync not being called when creating custom AuthorizeFilter that inherits from AuthorizeFilter

I've created a custom authorize filter which looks like this:
public class BearerTokenAuthorizeFilter : AuthorizeFilter
{
public override async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
await base.OnAuthorizationAsync(context);
if (context.Result is ChallengeResult)
{
// Then return a problem detail
ObjectResult result = new ObjectResult(new ProblemDetails
{
Type = ProblemDetailsTypes.Unauthorized,
Title = ReasonPhrases.GetReasonPhrase(StatusCodes.Status401Unauthorized),
Status = StatusCodes.Status401Unauthorized,
Detail = ProblemDetailsDescriptions.Unauthorized
});
result.ContentTypes.Add(new MediaTypeHeaderValue(new Microsoft.Extensions.Primitives.StringSegment("application/problem+json")));
context.Result = result;
await context.HttpContext.ChallengeAsync();
}
else if (context.Result is ForbidResult)
{
context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
await context.HttpContext.ForbidAsync();
}
}
}
I am registering this filter like this:
services.AddMvcCore(options =>
{
options.Filters.Add<BearerTokenAuthorizeFilter>();
});
I have set the default authentication to be 'Bearer':
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
I have added Authorize attribute on the controller. Whenever I send an unauthorized request to the endpoint my custom filter is never called and I have no idea why? My goal is to return problem details if the request is unauthorized to provide a little bit more information to the consumer than just the status code. Why is my filter not being called?
Try implement IAuthorizationFilter or IAsyncAuthorizationFilter instead of AuthorizeFilter. It work for me. Also I noticed that GetFilter(..) method returns AuthorizeFilter instance directly in AuthorizationApplicationModelProvider when filter class implements AuthorizeFilter. But when filter implements IAuthorizationFilter or IAsyncAuthorizationFilter this method being not called I think that is issue in ASP NET
I have ended up implementing my own IControllerModelConvention class which looks like this:
public class BearerTokenAuthorizeConvention : IControllerModelConvention
{
private AuthorizationPolicy _policy;
public BearerTokenAuthorizeConvention(AuthorizationPolicy policy)
{
_policy = policy;
}
public void Apply(ControllerModel controller)
{
if (controller.Filters.OfType<BearerTokenAuthorizeFilter>().FirstOrDefault() == null)
{
//default policy only used when there is no authorize filter in the controller
controller.Filters.Add(new BearerTokenAuthorizeFilter(_policy));
}
}
}
This will be executed once per controller. I then registered this convention like this:
// Configure application filters and conventions
services.Configure<MvcOptions>(options =>
{
AuthorizationPolicy defaultPolicy = new AuthorizationOptions().DefaultPolicy;
options.Conventions.Add(new BearerTokenAuthorizeConvention(defaultPolicy));
});
At this point every controller I have will be tagged with this custom filter which will call base implementation of AuthorizeFilter. The reason why I wanted to derive from AuthorizeFilter was because I wanted to call the default implementation of Authorize and then handle failed response on my own. I thought I could accomplish this very functionality and somehow still be able to only use Authorize attribute. This doesn't seem to be possible. Unless it is an I'm missing something?

Asp.net core API method is called on fluent api validation failed

I have implemented Fluent API validation with Aspnet Core and MediatR and disabled the default MVC validation.
Previously, On invalid data, the API validation will be called first and then API method will be called.
On invalid data, Fluent API Validation will throw an error and the call won't fired to the api method.
But now, even on invalid data, the api method is called.
what am I missing?
Configuration:
services.AddMvc().AddFluentValidation(fv =>
{
fv.RunDefaultMvcValidationAfterFluentValidationExecutes = false;
});
ValidatorOptions.Global.CascadeMode = CascadeMode.StopOnFirstFailure;
services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
Code:
public class LoginCommandValidator : AbstractValidator<LoginCommand>
{
public LoginCommandValidator(IStringLocalizer<Resource> stringLocalizer)
{
this.CascadeMode = CascadeMode.StopOnFirstFailure;
RuleFor(v => v.Username)
.NotEmpty().WithMessage(stringLocalizer["InvalidUsername"])
.NotNull().WithMessage(stringLocalizer["InvalidUsername"]);
RuleFor(v=>v.Password)
.NotEmpty().WithMessage(stringLocalizer["InvalidPassword"])
.NotNull().WithMessage(stringLocalizer["InvalidPassword"]);
}
}
Maybe you can write an ActionFilterAttribute, and then add this filter to your Controller.
Like this:
public class ValidateModelStateFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response = actionContext.Request.CreteErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}
Then add this filter above your controller:
[ValidateModelStateFilter]
Hope this can help you.

I cant show the API versions in response header with ApiVersioning .net Core

I follow the instruction REST API versioning with ASP.NET Core to show My API version in the response header.
This is my Configuration code:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddMvc().AddNewtonsoftJson();
services.AddMvc(opt =>
{
services.AddRouting(env => env.LowercaseUrls = true);
services.AddApiVersioning(opt => {
opt.ApiVersionReader = new MediaTypeApiVersionReader();
opt.AssumeDefaultVersionWhenUnspecified = true;
opt.ReportApiVersions = true;
opt.DefaultApiVersion = new ApiVersion(1, 0);
opt.ApiVersionSelector = new CurrentImplementationApiVersionSelector(opt);
});
}
and this is my Controller :
[Route("/")]
[ApiVersion("1.0")]
public class RootController:Controller
{
[HttpGet(Name =nameof(GetRoot))]
public IActionResult GetRoot()
{
var response = new { href = Url.Link(nameof(GetRoot),null) };
return Ok(response);
}
}
when I test my API with postman I got this result :
I don't know why opt.ReportApiVersions = true; doesn't work.
The reason why it behaves this way is to disambiguate an API controller from a UI controller. In ASP.NET Core, there's not really any other built-in way to do so as - a controller is a controller.
There are a few other ways to change this behavior:
Opt out with options.UseApiBehavior = false as the was the case before [ApiController]
Add a custom IApiControllerSpecification that identifies an API controller (there's a built-in implementation that understands [ApiController])
Replace the default IApiControllerFilter service, which is really just an aggregation over all registered IApiControllerSpecification implementations
I hope that helps
I found the solution. I have to add [ApiController] to my Controller:
[Route("/")]
[ApiVersion("1.0")]
[ApiController]
public class RootController:Controller
{
[HttpGet(Name =nameof(GetRoot))]
public IActionResult GetRoot()
{
var response = new { href = Url.Link(nameof(GetRoot),null) };
return Ok(response);
}
}

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());
});

Unable to create swagger.json file when using aspnet-api-versioning

I have .NET Core 2.2 application. I am trying to set up API with different versions using Microsoft.AspnetCore.Mvc.Versioning nugetpackage. I followed the samples provided in the repository.
I want to use an API version based on the name of the defining controller's namespace.
Project Structure
Controllers
namespace NetCoreApiVersioning.V1.Controllers
{
[ApiController]
[Route("[controller]")]
[Route("v{version:apiVersion}/[controller]")]
public class HelloWorldController : ControllerBase
{
public IActionResult Get()
{
return Ok();
}
}
}
namespace NetCoreApiVersioning.V2.Controllers
{
[ApiController]
[Route("[controller]")]
[Route("v{version:apiVersion}/[controller]")]
public class HelloWorldController : ControllerBase
{
public IActionResult Get()
{
return Ok();
}
}
}
Note the controllers does not have [ApiVersion] attribute becuase i want the versioning to be defined by the namespace
Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddApiVersioning(
options =>
{
// reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions"
options.ReportApiVersions = true;
// automatically applies an api version based on the name of the defining controller's namespace
options.Conventions.Add(new VersionByNamespaceConvention());
});
services.AddVersionedApiExplorer(
options =>
{
// add the versioned api explorer, which also adds IApiVersionDescriptionProvider service
// note: the specified format code will format the version as "'v'major[.minor][-status]"
options.GroupNameFormat = "'v'VVV";
// note: this option is only necessary when versioning by url segment. the SubstitutionFormat
// can also be used to control the format of the API version in route templates
options.SubstituteApiVersionInUrl = true;
});
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "API v1 ", Version = "v1" });
c.SwaggerDoc("v2", new Info { Title = "API v2", Version = "v2" });
});
// commented code below is from
// https://github.com/microsoft/aspnet-api-versioning/tree/master/samples/aspnetcore/SwaggerSample
//services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
//services.AddSwaggerGen(
// options =>
// {
// // add a custom operation filter which sets default values
// //options.OperationFilter<SwaggerDefaultValues>();
// // integrate xml comments
// //options.IncludeXmlComments(XmlCommentsFilePath);
// });
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApiVersionDescriptionProvider provider)
{
// remaining configuration omitted for brevity
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
app.UseSwaggerUI(
options =>
{
// build a swagger endpoint for each discovered API version
foreach (var description in provider.ApiVersionDescriptions)
{
options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
}
});
app.UseMvc();
}
}
Issue
It is not able to generate swagger.json file. When i browse url /swaggger i see error undefined /swagger/v1/swagger.json
found..
i am missing [HttpGet] attribute in ActionMethods