diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningExtensions.cs similarity index 59% rename from framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs rename to framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningExtensions.cs index 13148a4bc6..c239a42a45 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningExtensions.cs @@ -1,5 +1,7 @@ -using System.Linq; +using System; +using System.Linq; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.Versioning; using Volo.Abp.ApiVersioning; using Volo.Abp.AspNetCore.Mvc; @@ -8,31 +10,31 @@ using Volo.Abp.AspNetCore.Mvc.Versioning; namespace Microsoft.Extensions.DependencyInjection { - public static class AbpApiVersioningOptionsExtensions + public static class AbpApiVersioningExtensions { - public static void ConfigureAbp(this ApiVersioningOptions options, IServiceCollection services) + public static IServiceCollection AddAbpApiVersioning(this IServiceCollection services, Action setupAction) { - //TODO: Use new builder will be released with Api Versioning 2.1 instead of reflection! - services.AddTransient(); + services.AddTransient(); - services.Configure(op => - { - //TODO: Configuring api version should be done directly inside ConfigureAbp, - //TODO: not in a callback that will be called by MVC later! For that, we immediately need to controllerAssemblySettings + services.AddApiVersioning(setupAction); + + return services; + } - foreach (var setting in op.ConventionalControllers.ConventionalControllerSettings) + public static void ConfigureAbp(this ApiVersioningOptions options, AbpAspNetCoreMvcOptions mvcOptions) + { + foreach (var setting in mvcOptions.ConventionalControllers.ConventionalControllerSettings) + { + if (setting.ApiVersionConfigurer == null) { - if (setting.ApiVersionConfigurer == null) - { - ConfigureApiVersionsByConvention(options, setting); - } - else - { - setting.ApiVersionConfigurer.Invoke(options); - } + ConfigureApiVersionsByConvention(options, setting); } - }); + else + { + setting.ApiVersionConfigurer.Invoke(options); + } + } } private static void ConfigureApiVersionsByConvention(ApiVersioningOptions options, ConventionalControllerSetting setting) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs index 1a9646d13f..3eb7d18e39 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs @@ -85,7 +85,7 @@ namespace Volo.Abp.AspNetCore.Mvc method, apiDescription.RelativePath, apiDescription.HttpMethod, - setting?.ApiVersions.Select(v => v.ToString()).ToList() ?? new List() //TODO: Also get from ApiVersion attributes if available..? + GetSupportedVersions(controllerType, method, setting) )); AddParameterDescriptionsToModel(actionModel, method, apiDescription); @@ -126,6 +126,29 @@ namespace Volo.Abp.AspNetCore.Mvc return methodNameBuilder.ToString(); } + private static List GetSupportedVersions(Type controllerType, MethodInfo method, ConventionalControllerSetting setting) + { + var supportedVersions = new List(); + + var mapToAttributes = method.GetCustomAttributes().ToArray(); + if (mapToAttributes.Any()) + { + supportedVersions.AddRange( + mapToAttributes.SelectMany(a => a.Versions) + ); + } + else + { + supportedVersions.AddRange( + controllerType.GetCustomAttributes().SelectMany(a => a.Versions) + ); + + setting?.ApiVersions.ForEach(supportedVersions.Add); + } + + return supportedVersions.Select(v => v.ToString()).Distinct().ToList(); + } + private void AddParameterDescriptionsToModel(ActionApiDescriptionModel actionModel, MethodInfo method, ApiDescription apiDescription) { if (!apiDescription.ParameterDescriptions.Any()) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpConventionalApiControllerSpecification.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpConventionalApiControllerSpecification.cs new file mode 100644 index 0000000000..59b2184a53 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpConventionalApiControllerSpecification.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.Extensions.Options; + +namespace Volo.Abp.AspNetCore.Mvc.Conventions +{ + public class AbpConventionalApiControllerSpecification : IApiControllerSpecification + { + private readonly AbpAspNetCoreMvcOptions _options; + + public AbpConventionalApiControllerSpecification(IOptions options) + { + _options = options.Value; + } + + public bool IsSatisfiedBy(ControllerModel controller) + { + var configuration = _options + .ConventionalControllers + .ConventionalControllerSettings + .GetSettingOrNull(controller.ControllerType.AsType()); + + return configuration != null; + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs index 24a76e3766..3ea9103e9e 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs @@ -16,26 +16,34 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning )] public class AbpAspNetCoreMvcVersioningTestModule : AbpModule { - public override void ConfigureServices(ServiceConfigurationContext context) + public override void PreConfigureServices(ServiceConfigurationContext context) { - Configure(options => + PreConfigure(options => { //2.0 Version options.ConventionalControllers.Create(typeof(AbpAspNetCoreMvcVersioningTestModule).Assembly, opts => { - opts.TypePredicate = t => t.Namespace == typeof(Volo.Abp.AspNetCore.Mvc.Versioning.App.TodoAppService).Namespace; + opts.TypePredicate = t => t.Namespace == typeof(Volo.Abp.AspNetCore.Mvc.Versioning.App.v2.TodoAppService).Namespace; opts.ApiVersions.Add(new ApiVersion(2, 0)); }); //1.0 Compatibility version options.ConventionalControllers.Create(typeof(AbpAspNetCoreMvcVersioningTestModule).Assembly, opts => { - opts.TypePredicate = t => t.Namespace == typeof(Volo.Abp.AspNetCore.Mvc.Versioning.App.Compat.TodoAppService).Namespace; + opts.TypePredicate = t => t.Namespace == typeof(Volo.Abp.AspNetCore.Mvc.Versioning.App.v1.TodoAppService).Namespace; opts.ApiVersions.Add(new ApiVersion(1, 0)); }); }); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + context.Services.ExecutePreConfiguredActions(options); + }); - context.Services.AddApiVersioning(options => + context.Services.AddAbpApiVersioning(options => { options.ReportApiVersions = true; options.AssumeDefaultVersionWhenUnspecified = true; @@ -43,7 +51,8 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning //options.ApiVersionReader = new HeaderApiVersionReader("api-version"); //Supports header too //options.ApiVersionReader = new MediaTypeApiVersionReader(); //Supports accept header too - options.ConfigureAbp(context.Services); + var mvcOptions = context.Services.ExecutePreConfiguredActions(); + options.ConfigureAbp(mvcOptions); }); context.Services.AddHttpClientProxies(typeof(AbpAspNetCoreMvcVersioningTestModule).Assembly); diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/HelloController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/HelloController.cs index fb39ae53ed..db652e95bd 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/HelloController.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/HelloController.cs @@ -5,13 +5,33 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning.App { [ApiVersion("1.0")] [ApiVersion("2.0")] + [ApiController] [Route("api/v{apiVersion:apiVersion}/[controller]")] public class HelloController : AbpController, IHelloController { + [HttpGet] + public Task GetAsync() + { + return Task.FromResult($"Get-{HttpContext.GetRequestedApiVersion().ToString()}"); + } + + [HttpPost] + [MapToApiVersion("1.0")] + public Task PostAsyncV1() + { + return PostAsync(); + } + [HttpPost] - public Task PostAsync() + [MapToApiVersion("2.0")] + public Task PostAsyncV2() + { + return PostAsync(); + } + + private Task PostAsync() { - return Task.FromResult($"42-{HttpContext.GetRequestedApiVersion().ToString()}"); + return Task.FromResult($"Post-{HttpContext.GetRequestedApiVersion().ToString()}"); } } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/IHelloController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/IHelloController.cs index bd4871aac6..17c2226edd 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/IHelloController.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/IHelloController.cs @@ -4,6 +4,10 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning.App { public interface IHelloController : IRemoteService { - Task PostAsync(); + Task GetAsync(); + + Task PostAsyncV1(); + + Task PostAsyncV2(); } } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/ITodoAppService.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v1/ITodoAppService.cs similarity index 72% rename from framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/ITodoAppService.cs rename to framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v1/ITodoAppService.cs index f4de87629d..3e82da0329 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/ITodoAppService.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v1/ITodoAppService.cs @@ -1,6 +1,6 @@ using Volo.Abp.Application.Services; -namespace Volo.Abp.AspNetCore.Mvc.Versioning.App +namespace Volo.Abp.AspNetCore.Mvc.Versioning.App.v1 { public interface ITodoAppService : IApplicationService { diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/TodoAppService.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v1/TodoAppService.cs similarity index 82% rename from framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/TodoAppService.cs rename to framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v1/TodoAppService.cs index 8d25b6facd..f3f900148d 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/TodoAppService.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v1/TodoAppService.cs @@ -1,7 +1,7 @@ using Volo.Abp.ApiVersioning; using Volo.Abp.Application.Services; -namespace Volo.Abp.AspNetCore.Mvc.Versioning.App.Compat +namespace Volo.Abp.AspNetCore.Mvc.Versioning.App.v1 { public class TodoAppService : ApplicationService, ITodoAppService { @@ -14,7 +14,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning.App.Compat public string Get(int id) { - return "Compat-" + id + "-" + GetVersionOrNone(); + return $"Compat-{id}-{GetVersionOrNone()}"; } private string GetVersionOrNone() diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/ITodoAppService.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v2/ITodoAppService.cs similarity index 70% rename from framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/ITodoAppService.cs rename to framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v2/ITodoAppService.cs index 0a8ed290ce..ae4afea6e1 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/ITodoAppService.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v2/ITodoAppService.cs @@ -1,6 +1,6 @@ using Volo.Abp.Application.Services; -namespace Volo.Abp.AspNetCore.Mvc.Versioning.App.Compat +namespace Volo.Abp.AspNetCore.Mvc.Versioning.App.v2 { public interface ITodoAppService : IApplicationService { diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/TodoAppService.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v2/TodoAppService.cs similarity index 92% rename from framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/TodoAppService.cs rename to framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v2/TodoAppService.cs index e4b08eed99..b97af873b7 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/TodoAppService.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v2/TodoAppService.cs @@ -1,7 +1,7 @@ using Volo.Abp.ApiVersioning; using Volo.Abp.Application.Services; -namespace Volo.Abp.AspNetCore.Mvc.Versioning.App +namespace Volo.Abp.AspNetCore.Mvc.Versioning.App.v2 { public class TodoAppService : ApplicationService, ITodoAppService { diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/HelloController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/HelloController_Tests.cs index 519bfffd11..0dc4057bde 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/HelloController_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/HelloController_Tests.cs @@ -8,17 +8,29 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning.Test { public class HelloController_Tests: AspNetCoreMvcVersioningTestBase { - private readonly IHelloController _todoAppService; + private readonly IHelloController _helloController; public HelloController_Tests() { - _todoAppService = ServiceProvider.GetRequiredService(); + _helloController = ServiceProvider.GetRequiredService(); } [Fact] - public async Task PostAsync() + public async Task GetAsync() { - (await _todoAppService.PostAsync()).ShouldBe("42-2.0"); + (await _helloController.GetAsync()).ShouldBe("Get-2.0"); + } + + [Fact] + public async Task PostAsyncV1() + { + (await _helloController.PostAsyncV1()).ShouldBe("Post-1.0"); + } + + [Fact] + public async Task PostAsyncV2() + { + (await _helloController.PostAsyncV2()).ShouldBe("Post-2.0"); } } } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/Compat/TodoAppService_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/v1/TodoAppService_Tests.cs similarity index 69% rename from framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/Compat/TodoAppService_Tests.cs rename to framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/v1/TodoAppService_Tests.cs index 3d88941997..7b461a3902 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/Compat/TodoAppService_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/v1/TodoAppService_Tests.cs @@ -1,9 +1,9 @@ using Microsoft.Extensions.DependencyInjection; using Shouldly; -using Volo.Abp.AspNetCore.Mvc.Versioning.App.Compat; +using Volo.Abp.AspNetCore.Mvc.Versioning.App.v1; using Xunit; -namespace Volo.Abp.AspNetCore.Mvc.Versioning.Test.Compat +namespace Volo.Abp.AspNetCore.Mvc.Versioning.Test.v1 { public class TodoAppService_Tests : AspNetCoreMvcVersioningTestBase { @@ -14,7 +14,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning.Test.Compat _todoAppService = ServiceProvider.GetRequiredService(); } - [Fact(Skip = "It stopped working after ASP.NET Core 2.2 Upgrade. Should work on that.")] + [Fact] public void Get() { _todoAppService.Get(42).ShouldBe("Compat-42-1.0"); diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/TodoAppService_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/v2/TodoAppService_Tests.cs similarity index 71% rename from framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/TodoAppService_Tests.cs rename to framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/v2/TodoAppService_Tests.cs index 2bccbd41b2..f4cd122afc 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/TodoAppService_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/v2/TodoAppService_Tests.cs @@ -1,9 +1,9 @@ using Microsoft.Extensions.DependencyInjection; using Shouldly; -using Volo.Abp.AspNetCore.Mvc.Versioning.App; +using Volo.Abp.AspNetCore.Mvc.Versioning.App.v2; using Xunit; -namespace Volo.Abp.AspNetCore.Mvc.Versioning.Test +namespace Volo.Abp.AspNetCore.Mvc.Versioning.Test.v2 { public class TodoAppService_Tests : AspNetCoreMvcVersioningTestBase { @@ -14,7 +14,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning.Test _todoAppService = ServiceProvider.GetRequiredService(); } - [Fact(Skip = "It stopped working after ASP.NET Core 2.2 Upgrade. Should work on that.")] + [Fact] public void Get() { _todoAppService.Get(42).ShouldBe("42-2.0");