From 326a673703940fd222e01d66ebcc50117412ae8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 29 Sep 2017 15:59:43 +0300 Subject: [PATCH 01/23] Refactor route on AbpApiDefinitionController. --- .../AspNetCore/Mvc/ApiExploring/AbpApiDefinitionController.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpApiDefinitionController.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpApiDefinitionController.cs index 869f1ba136..493eaef389 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpApiDefinitionController.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpApiDefinitionController.cs @@ -3,7 +3,7 @@ using Volo.Abp.Http.Modeling; namespace Volo.Abp.AspNetCore.Mvc.ApiExploring { - [Area("abp")] + [Route("api/abp/api-description")] public class AbpApiDefinitionController : AbpController { private readonly IApiDescriptionModelProvider _modelProvider; @@ -14,7 +14,6 @@ namespace Volo.Abp.AspNetCore.Mvc.ApiExploring } [HttpGet] - [Route("api/abp/api-description")] public ApplicationApiDescriptionModel Get() { return _modelProvider.CreateApiModel(); From 01fead68a51a1f95785bdeddc62f4ea5e339b135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 29 Sep 2017 17:24:44 +0300 Subject: [PATCH 02/23] Add configuration to set version to all app services in a module. --- .../AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs | 2 +- .../AbpApiVersioningOptionsExtensions.cs | 33 +++++++ .../Volo.Abp.AspNetCore.Mvc.csproj | 1 + .../AbpAppServiceControllerFeatureProvider.cs | 7 +- .../Mvc/AbpControllerAssemblySetting.cs | 51 +++++++++- .../AbpControllerAssemblySettingBuilder.cs | 39 -------- .../Mvc/AppServiceControllerOptions.cs | 9 +- .../IAbpControllerAssemblySettingBuilder.cs | 12 --- .../AbpIdentityHttpApiHostModule.cs | 98 +++++++++++++++++-- .../Controllers/HomeController.cs | 2 + .../SwaggerDefaultValues.cs | 41 ++++++++ .../VersioningTests/CallsController.cs | 28 ------ .../VersioningTests/V1/CallsController.cs | 55 +++++++++++ .../Volo.Abp.Identity.HttpApi.Host.csproj | 1 + .../Volo.Abp.Identity.HttpApi.csproj | 4 + .../Abp/Identity/AbpIdentityHttpApiModule.cs | 11 ++- .../App/AbpAspNetCoreMvcTestModule.cs | 16 ++- 17 files changed, 304 insertions(+), 106 deletions(-) create mode 100644 src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs delete mode 100644 src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySettingBuilder.cs delete mode 100644 src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/IAbpControllerAssemblySettingBuilder.cs create mode 100644 src/Volo.Abp.Identity.HttpApi.Host/SwaggerDefaultValues.cs delete mode 100644 src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/CallsController.cs create mode 100644 src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V1/CallsController.cs diff --git a/src/AbpDesk/AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs b/src/AbpDesk/AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs index feaf44e07a..24addb659f 100644 --- a/src/AbpDesk/AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs +++ b/src/AbpDesk/AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs @@ -65,7 +65,7 @@ namespace AbpDesk.Web.Mvc services.Configure(options => { - options.AppServiceControllers.CreateFor(typeof(AbpDeskApplicationModule).Assembly); + options.AppServiceControllers.Create(typeof(AbpDeskApplicationModule).Assembly); }); } diff --git a/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs b/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs new file mode 100644 index 0000000000..d72ec24d95 --- /dev/null +++ b/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Versioning; +using Microsoft.AspNetCore.Mvc.Versioning.Conventions; +using Volo.Abp.AspNetCore.Mvc; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class AbpApiVersioningOptionsExtensions + { + public static void AddAbpModules(this ApiVersioningOptions options, IServiceCollection services) + { + services.Configure(op => + { + foreach (var setting in op.AppServiceControllers.ControllerAssemblySettings) + { + foreach (var controllerType in setting.ControllerTypes) + { + var controllerBuilder = typeof(ApiVersionConventionBuilder) + .GetMethod(nameof(ApiVersionConventionBuilder.Controller), BindingFlags.Instance | BindingFlags.Public) + .MakeGenericMethod(controllerType) + .Invoke(options.Conventions, null); + + typeof(ControllerApiVersionConventionBuilder<>) + .MakeGenericType(controllerType) + .GetMethod("HasApiVersion") + .Invoke(controllerBuilder, new object[] { setting.ApiVersion }); + } + } + }); + } + } +} diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj b/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj index 746c1b2bef..0eada12839 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj @@ -20,6 +20,7 @@ + diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceControllerFeatureProvider.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceControllerFeatureProvider.cs index 376268eef9..409ad3241f 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceControllerFeatureProvider.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceControllerFeatureProvider.cs @@ -34,7 +34,12 @@ namespace Volo.Abp.AspNetCore.Mvc } //TODO: Move this to a lazy loaded field for efficiency. - var configuration = _application.ServiceProvider.GetRequiredService>().Value.AppServiceControllers.ControllerAssemblySettings.GetSettingOrNull(type); + var configuration = _application.ServiceProvider + .GetRequiredService>().Value + .AppServiceControllers + .ControllerAssemblySettings + .GetSettingOrNull(type); + return configuration != null && (configuration.TypePredicate == null || configuration.TypePredicate(type)); } } diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs index cad6332995..eb3cdd144b 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs @@ -1,7 +1,12 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Reflection; using JetBrains.Annotations; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Volo.Abp.Application.Services; +using Volo.Abp.Reflection; namespace Volo.Abp.AspNetCore.Mvc { @@ -11,7 +16,19 @@ namespace Volo.Abp.AspNetCore.Mvc public Assembly Assembly { get; } [NotNull] - public string RootPath { get; } + public List ControllerTypes { get; } + + [NotNull] + public string RootPath + { + get => _rootPath; + set + { + Check.NotNull(value, nameof(value)); + _rootPath = value; + } + } + private string _rootPath; [CanBeNull] public Func TypePredicate { get; set; } @@ -25,12 +42,44 @@ namespace Volo.Abp.AspNetCore.Mvc [CanBeNull] public Func UrlActionNameNormalizer { get; set; } + public ApiVersion ApiVersion { get; set; } + public AbpControllerAssemblySetting([NotNull] Assembly assembly, [NotNull] string rootPath) { Check.NotNull(assembly, rootPath); Assembly = assembly; RootPath = rootPath; + + ControllerTypes = new List(); + + ApiVersion = new ApiVersion(1, 0); + } + + public void Initialize() + { + ControllerTypes.AddRange( + Assembly.GetTypes() + .Where(IsRemoteService) + .WhereIf(TypePredicate != null, TypePredicate) + ); + } + + private static bool IsRemoteService(Type type) + { + if (!typeof(IRemoteService).IsAssignableFrom(type) || !type.IsPublic || type.IsAbstract || type.IsGenericType) + { + return false; + } + + var remoteServiceAttr = ReflectionHelper.GetSingleAttributeOrDefault(type); + + if (remoteServiceAttr != null && !remoteServiceAttr.IsEnabledFor(type)) + { + return false; + } + + return true; } } } \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySettingBuilder.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySettingBuilder.cs deleted file mode 100644 index ea45af6181..0000000000 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySettingBuilder.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using Microsoft.AspNetCore.Mvc.ApplicationModels; - -namespace Volo.Abp.AspNetCore.Mvc -{ - public class AbpControllerAssemblySettingBuilder : IAbpControllerAssemblySettingBuilder - { - private readonly AbpControllerAssemblySetting _setting; - - public AbpControllerAssemblySettingBuilder(AbpControllerAssemblySetting setting) - { - _setting = setting; - } - - public AbpControllerAssemblySettingBuilder Where(Func predicate) - { - _setting.TypePredicate = predicate; - return this; - } - - public AbpControllerAssemblySettingBuilder ConfigureControllerModel(Action configurer) - { - _setting.ControllerModelConfigurer = configurer; - return this; - } - - public AbpControllerAssemblySettingBuilder NormalizeControllerNameInUrl(Func normalizer) - { - _setting.UrlControllerNameNormalizer = normalizer; - return this; - } - - public AbpControllerAssemblySettingBuilder NormalizeActionNameInUrl(Func normalizer) - { - _setting.UrlActionNameNormalizer = normalizer; - return this; - } - } -} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AppServiceControllerOptions.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AppServiceControllerOptions.cs index 8e118ae561..4c677fb8e0 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AppServiceControllerOptions.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AppServiceControllerOptions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using JetBrains.Annotations; using Microsoft.AspNetCore.Http; using Volo.Abp.Http.Modeling; @@ -22,11 +23,13 @@ namespace Volo.Abp.AspNetCore.Mvc }; } - public AbpControllerAssemblySettingBuilder CreateFor(Assembly assembly, string rootPath = ModuleApiDescriptionModel.DefaultRootPath) + public AppServiceControllerOptions Create(Assembly assembly, [CanBeNull] Action optionsAction = null) { - var setting = new AbpControllerAssemblySetting(assembly, rootPath); + var setting = new AbpControllerAssemblySetting(assembly, ModuleApiDescriptionModel.DefaultRootPath); + optionsAction?.Invoke(setting); + setting.Initialize(); ControllerAssemblySettings.Add(setting); - return new AbpControllerAssemblySettingBuilder(setting); + return this; } } } \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/IAbpControllerAssemblySettingBuilder.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/IAbpControllerAssemblySettingBuilder.cs deleted file mode 100644 index ccd85b3b9a..0000000000 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/IAbpControllerAssemblySettingBuilder.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using Microsoft.AspNetCore.Mvc.ApplicationModels; - -namespace Volo.Abp.AspNetCore.Mvc -{ - public interface IAbpControllerAssemblySettingBuilder - { - AbpControllerAssemblySettingBuilder Where(Func predicate); - - AbpControllerAssemblySettingBuilder ConfigureControllerModel(Action configurer); - } -} \ No newline at end of file diff --git a/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs b/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs index aca4f8ca56..01b14f1216 100644 --- a/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs +++ b/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs @@ -1,11 +1,21 @@ -using Microsoft.AspNetCore.Builder; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.AspNetCore.Mvc.RazorPages.Internal; +using Microsoft.AspNetCore.Mvc.Versioning; +using Microsoft.AspNetCore.Mvc.Versioning.Conventions; using Microsoft.EntityFrameworkCore; +using Microsoft.Examples; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Swashbuckle.AspNetCore.Swagger; using Volo.Abp.AspNetCore.Modularity; +using Volo.Abp.AspNetCore.Mvc.ApiExploring; using Volo.Abp.Autofac; using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; @@ -19,7 +29,7 @@ namespace Volo.Abp.Identity.HttpApi.Host { public override void ConfigureServices(IServiceCollection services) { - var hostingEnvironment = services.GetSingletonInstance(); + var hostingEnvironment = services.GetSingletonInstance(); //TOD: Move to BuildConfiguration method var configuration = BuildConfiguration(hostingEnvironment); services.Configure(configuration); @@ -33,14 +43,56 @@ namespace Volo.Abp.Identity.HttpApi.Host }); }); - services.AddMvc(); + // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service + // note: the specified format code will format the version as "'v'major[.minor][-status]" + services.AddMvcCore().AddVersionedApiExplorer(o => o.GroupNameFormat = "'v'VVV"); - services.AddSwaggerGen(options => + services.AddMvc(options => { - options.SwaggerDoc("v1", new Info { Title = "Volo.Abp.Identity API", Version = "v1" }); - options.DocInclusionPredicate((docName, description) => true); + }); + services.AddApiVersioning(o => + { + o.ReportApiVersions = true; + o.Conventions.Controller().IsApiVersionNeutral(); + //o.Conventions.Controller().HasApiVersion(new ApiVersion(3, 0)); //We can do that based on controller's AbpApiVersion attribute! + o.AssumeDefaultVersionWhenUnspecified = true; + o.DefaultApiVersion = new ApiVersion(3, 0); //Default: 1.0 //We can not rely on that, application should do. + + //o.ErrorResponses //TOD: We can override error response generator (to solve https://github.com/Microsoft/aspnet-api-versioning/issues/195) + + //o.Conventions.Controller().HasApiVersion(2, 0); + //o.Conventions.Controller().IsApiVersionNeutral(); + + o.AddAbpModules(services); + }); + + services.AddSwaggerGen( + options => + { + //options.SwaggerDoc("v1", new Info { Title = "Volo.Abp.Identity API", Version = "v1" }); + //options.DocInclusionPredicate((docName, description) => true); + + + // resolve the IApiVersionDescriptionProvider service + // note: that we have to build a temporary service provider here because one has not been created yet + var provider = services.BuildServiceProvider().GetRequiredService(); + + // add a swagger document for each discovered API version + // note: you might choose to skip or document deprecated API versions differently + foreach (var description in provider.ApiVersionDescriptions) + { + options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description)); + } + + // add a custom operation filter which sets default values + options.OperationFilter(); + + // integrate xml comments + //options.IncludeXmlComments(XmlCommentsFilePath); //TODO: Add XML comments! + }); + services.AddAssemblyOf(); } @@ -57,10 +109,20 @@ namespace Volo.Abp.Identity.HttpApi.Host app.UseStaticFiles(); + var provider = context.ServiceProvider.GetRequiredService(); + app.UseSwagger(); - app.UseSwaggerUI(c => + app.UseSwaggerUI(options => { - c.SwaggerEndpoint("/swagger/v1/swagger.json", "Volo.Abp.Identity API"); + //options.SwaggerEndpoint("/swagger/v1/swagger.json", "Volo.Abp.Identity API"); + + + + // 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.UseMvcWithDefaultRoute(); @@ -75,5 +137,25 @@ namespace Volo.Abp.Identity.HttpApi.Host return builder.Build(); } + + private static Info CreateInfoForApiVersion(ApiVersionDescription description) + { + var info = new Info() + { + Title = $"Sample API {description.ApiVersion}", + Version = description.ApiVersion.ToString(), + Description = "A sample application with Swagger, Swashbuckle, and API versioning.", + Contact = new Contact() { Name = "Bill Mei", Email = "bill.mei@somewhere.com" }, + TermsOfService = "Shareware", + License = new License() { Name = "MIT", Url = "https://opensource.org/licenses/MIT" } + }; + + if (description.IsDeprecated) + { + info.Description += " This API version has been deprecated."; + } + + return info; + } } } diff --git a/src/Volo.Abp.Identity.HttpApi.Host/Controllers/HomeController.cs b/src/Volo.Abp.Identity.HttpApi.Host/Controllers/HomeController.cs index 34283e4d36..2a8c3aed0b 100644 --- a/src/Volo.Abp.Identity.HttpApi.Host/Controllers/HomeController.cs +++ b/src/Volo.Abp.Identity.HttpApi.Host/Controllers/HomeController.cs @@ -5,6 +5,8 @@ namespace Volo.Abp.Identity.HttpApi.Host.Controllers { public class HomeController : AbpController { + [HttpPost] + [Route("/api/v1/users/create")] public IActionResult Index() { return Redirect("/swagger"); diff --git a/src/Volo.Abp.Identity.HttpApi.Host/SwaggerDefaultValues.cs b/src/Volo.Abp.Identity.HttpApi.Host/SwaggerDefaultValues.cs new file mode 100644 index 0000000000..11045d85a6 --- /dev/null +++ b/src/Volo.Abp.Identity.HttpApi.Host/SwaggerDefaultValues.cs @@ -0,0 +1,41 @@ +namespace Microsoft.Examples +{ + using Swashbuckle.AspNetCore.Swagger; + using Swashbuckle.AspNetCore.SwaggerGen; + using System.Linq; + + /// + /// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter. + /// + /// This is only required due to bugs in the . + /// Once they are fixed and published, this class can be removed. + public class SwaggerDefaultValues : IOperationFilter + { + /// + /// Applies the filter to the specified operation using the given context. + /// + /// The operation to apply the filter to. + /// The current operation filter context. + public void Apply(Operation operation, OperationFilterContext context) + { + // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412 + // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413 + foreach (var parameter in operation.Parameters.OfType()) + { + var description = context.ApiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name); + + if (parameter.Description == null) + { + parameter.Description = description.ModelMetadata.Description; + } + + if (parameter.Default == null) + { + parameter.Default = description.RouteInfo?.DefaultValue; + } + + parameter.Required |= description.RouteInfo?.IsOptional == true; + } + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/CallsController.cs b/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/CallsController.cs deleted file mode 100644 index cf590e964a..0000000000 --- a/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/CallsController.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Collections.Generic; -using Microsoft.AspNetCore.Mvc; -using Volo.Abp.Application.Dtos; -using Volo.Abp.AspNetCore.Mvc; - -namespace Volo.Abp.Identity.HttpApi.Host.VersioningTests -{ - [Route("api/calls")] - public class CallsController : AbpController - { - private static List _calls = new List - { - new CallDto {Id = 1, Number = "123456"}, - new CallDto { Id = 2, Number = "123457" } - }; - - [HttpGet] - public List GetList() - { - return _calls; - } - } - - public class CallDto : EntityDto - { - public string Number { get; set; } - } -} diff --git a/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V1/CallsController.cs b/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V1/CallsController.cs new file mode 100644 index 0000000000..79073d8ba7 --- /dev/null +++ b/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V1/CallsController.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Application.Dtos; +using Volo.Abp.AspNetCore.Mvc; + +namespace Volo.Abp.Identity.HttpApi.Host.VersioningTests.V1 +{ + [ApiVersion("1.0")] + [Route("api/v{api-version:apiVersion}/calls")] + public class CallsController : AbpController + { + private static List _calls = new List + { + new CallDto {Id = 1, Number = "123456"}, + new CallDto { Id = 2, Number = "123457" } + }; + + [HttpGet] + public List GetList() + { + return _calls; + } + } + + public class CallDto : EntityDto + { + public string Number { get; set; } + } + + [ApiVersion("2.0")] + [Route("api/v{api-version:apiVersion}/calls")] + [ControllerName("Calls")] + public class Calls2Controller : AbpController + { + private static List _calls = new List + { + new CallDto {Id = 1, Number = "123456000"}, + new CallDto { Id = 2, Number = "123457000" } + }; + + [HttpGet] + public List GetList() + { + return _calls; + } + + [HttpGet] + [Route("by-filter")] + public List GetList(string num) + { + return _calls.Where(c => c.Number.Contains(num)).ToList(); + } + } +} diff --git a/src/Volo.Abp.Identity.HttpApi.Host/Volo.Abp.Identity.HttpApi.Host.csproj b/src/Volo.Abp.Identity.HttpApi.Host/Volo.Abp.Identity.HttpApi.Host.csproj index 581749f373..18f3574de5 100644 --- a/src/Volo.Abp.Identity.HttpApi.Host/Volo.Abp.Identity.HttpApi.Host.csproj +++ b/src/Volo.Abp.Identity.HttpApi.Host/Volo.Abp.Identity.HttpApi.Host.csproj @@ -26,6 +26,7 @@ + diff --git a/src/Volo.Abp.Identity.HttpApi/Volo.Abp.Identity.HttpApi.csproj b/src/Volo.Abp.Identity.HttpApi/Volo.Abp.Identity.HttpApi.csproj index 73ab6aa798..8763a68a14 100644 --- a/src/Volo.Abp.Identity.HttpApi/Volo.Abp.Identity.HttpApi.csproj +++ b/src/Volo.Abp.Identity.HttpApi/Volo.Abp.Identity.HttpApi.csproj @@ -13,6 +13,10 @@ + + + + diff --git a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs index 30e3996bc1..e0e74595e6 100644 --- a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs +++ b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs @@ -1,4 +1,5 @@ using System; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.Modularity; @@ -14,10 +15,12 @@ namespace Volo.Abp.Identity services.Configure(options => { - options - .AppServiceControllers - .CreateFor(typeof(AbpIdentityApplicationModule).Assembly, "identity") - .NormalizeControllerNameInUrl(context => context.ControllerName.RemovePreFix("Identity")); + options.AppServiceControllers.Create(typeof(AbpIdentityApplicationModule).Assembly, opts => + { + opts.RootPath = "identity"; + opts.UrlControllerNameNormalizer = context => context.ControllerName.RemovePreFix("Identity"); + opts.ApiVersion = new ApiVersion(2, 0, "beta"); + }); }); } } diff --git a/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/AbpAspNetCoreMvcTestModule.cs b/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/AbpAspNetCoreMvcTestModule.cs index a3d999671e..e6cf1598b8 100644 --- a/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/AbpAspNetCoreMvcTestModule.cs +++ b/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/AbpAspNetCoreMvcTestModule.cs @@ -25,15 +25,13 @@ namespace Volo.Abp.AspNetCore.App services.Configure(options => { - options - .AppServiceControllers - .CreateFor(typeof(TestAppModule).Assembly) - .NormalizeActionNameInUrl( - context => - string.Equals(context.ActionNameInUrl, "phone", StringComparison.OrdinalIgnoreCase) - ? "phones" - : context.ActionNameInUrl - ); + options.AppServiceControllers.Create(typeof(TestAppModule).Assembly, opts => + { + opts.UrlActionNameNormalizer = context => + string.Equals(context.ActionNameInUrl, "phone", StringComparison.OrdinalIgnoreCase) + ? "phones" + : context.ActionNameInUrl; + }); }); services.AddAssemblyOf(); From 7ab042274812045442cab4f23d1adbdc56b89b7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 29 Sep 2017 17:46:54 +0300 Subject: [PATCH 03/23] Improvements on versioning --- .../AbpAppServiceControllerFeatureProvider.cs | 21 ++----------------- .../AspNetCore/Mvc/AbpAppServiceConvention.cs | 9 +++++--- .../Mvc/AbpControllerAssemblySetting.cs | 17 ++++++++------- .../AspNetCoreApiDescriptionModelProvider.cs | 2 +- .../Mvc/ControllerAssemblySettingList.cs | 2 +- .../Abp/Identity/AbpIdentityHttpApiModule.cs | 4 ++-- 6 files changed, 22 insertions(+), 33 deletions(-) diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceControllerFeatureProvider.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceControllerFeatureProvider.cs index 409ad3241f..a9165f1409 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceControllerFeatureProvider.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceControllerFeatureProvider.cs @@ -2,8 +2,6 @@ using System.Reflection; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using Volo.Abp.Application.Services; -using Volo.Abp.Reflection; namespace Volo.Abp.AspNetCore.Mvc { @@ -18,29 +16,14 @@ namespace Volo.Abp.AspNetCore.Mvc protected override bool IsController(TypeInfo typeInfo) { - var type = typeInfo.AsType(); - - if (!typeof(IRemoteService).IsAssignableFrom(type) || - !typeInfo.IsPublic || typeInfo.IsAbstract || typeInfo.IsGenericType) - { - return false; - } - - var remoteServiceAttr = ReflectionHelper.GetSingleAttributeOrDefault(typeInfo); - - if (remoteServiceAttr != null && !remoteServiceAttr.IsEnabledFor(type)) - { - return false; - } - //TODO: Move this to a lazy loaded field for efficiency. var configuration = _application.ServiceProvider .GetRequiredService>().Value .AppServiceControllers .ControllerAssemblySettings - .GetSettingOrNull(type); + .GetSettingOrNull(typeInfo.AsType()); - return configuration != null && (configuration.TypePredicate == null || configuration.TypePredicate(type)); + return configuration != null; } } } \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs index 98a9daca43..0952934df6 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs @@ -37,11 +37,14 @@ namespace Volo.Abp.AspNetCore.Mvc var controllerType = controller.ControllerType.AsType(); var configuration = GetControllerSettingOrNull(controllerType); - if (IsRemoteService(controllerType)) + //TODO: We can remove different behaviour for ImplementsRemoteServiceInterface. If there is a configuration, then it should be applied! + //TODO: If so, we can rename AppServiceControllers to ConventionalControllers! + //TODO: But also consider AbpControllerAssemblySetting.IsRemoteService method too..! + + if (ImplementsRemoteServiceInterface(controllerType)) { controller.ControllerName = controller.ControllerName.RemovePostFix(ApplicationService.CommonPostfixes); configuration?.ControllerModelConfigurer?.Invoke(controller); - //ConfigureArea(controller, configuration); ConfigureRemoteService(controller, configuration); } else @@ -328,7 +331,7 @@ namespace Volo.Abp.AspNetCore.Mvc return selector.AttributeRouteModel == null && selector.ActionConstraints.IsNullOrEmpty(); } - protected virtual bool IsRemoteService(Type controllerType) + protected virtual bool ImplementsRemoteServiceInterface(Type controllerType) { return typeof(IRemoteService).GetTypeInfo().IsAssignableFrom(controllerType); } diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs index eb3cdd144b..a99b87855e 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs @@ -16,7 +16,7 @@ namespace Volo.Abp.AspNetCore.Mvc public Assembly Assembly { get; } [NotNull] - public List ControllerTypes { get; } + public HashSet ControllerTypes { get; } [NotNull] public string RootPath @@ -51,18 +51,21 @@ namespace Volo.Abp.AspNetCore.Mvc Assembly = assembly; RootPath = rootPath; - ControllerTypes = new List(); + ControllerTypes = new HashSet(); ApiVersion = new ApiVersion(1, 0); } public void Initialize() { - ControllerTypes.AddRange( - Assembly.GetTypes() - .Where(IsRemoteService) - .WhereIf(TypePredicate != null, TypePredicate) - ); + var types = Assembly.GetTypes() + .Where(IsRemoteService) + .WhereIf(TypePredicate != null, TypePredicate); + + foreach (var type in types) + { + ControllerTypes.Add(type); + } } private static bool IsRemoteService(Type type) diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs index 6179f860c6..3a2fdba255 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs @@ -164,7 +164,7 @@ namespace Volo.Abp.AspNetCore.Mvc foreach (var controllerSetting in _options.AppServiceControllers.ControllerAssemblySettings) { - if (Equals(controllerType.Assembly, controllerSetting.Assembly)) + if(controllerSetting.ControllerTypes.Contains(controllerType)) { return controllerSetting.RootPath; } diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ControllerAssemblySettingList.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ControllerAssemblySettingList.cs index c7701994bc..b59ac8c576 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ControllerAssemblySettingList.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ControllerAssemblySettingList.cs @@ -10,7 +10,7 @@ namespace Volo.Abp.AspNetCore.Mvc [CanBeNull] public AbpControllerAssemblySetting GetSettingOrNull(Type controllerType) { - return this.FirstOrDefault(controllerSetting => controllerSetting.Assembly == controllerType.Assembly); + return this.FirstOrDefault(controllerSetting => controllerSetting.ControllerTypes.Contains(controllerType)); } } } \ No newline at end of file diff --git a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs index e0e74595e6..9a450b775c 100644 --- a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs +++ b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs @@ -1,5 +1,4 @@ using System; -using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.Modularity; @@ -19,7 +18,8 @@ namespace Volo.Abp.Identity { opts.RootPath = "identity"; opts.UrlControllerNameNormalizer = context => context.ControllerName.RemovePreFix("Identity"); - opts.ApiVersion = new ApiVersion(2, 0, "beta"); + + // }); }); } From 1b9c2974ac9dca610cbc91435339bdc3b5bc0d2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 29 Sep 2017 17:49:54 +0300 Subject: [PATCH 04/23] Added TODO. --- .../Volo/Abp/Identity/AbpIdentityHttpApiModule.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs index 9a450b775c..f452e08507 100644 --- a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs +++ b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs @@ -18,10 +18,11 @@ namespace Volo.Abp.Identity { opts.RootPath = "identity"; opts.UrlControllerNameNormalizer = context => context.ControllerName.RemovePreFix("Identity"); - - // }); }); + + //TODO: Allow to use Api Versioning's API to explicitly configure versioning for app services and other controllers, + //TODO: rather than implicitly doing it via AppServiceControllers.Create call above! } } } \ No newline at end of file From e511f8a0f2fe24f550d881e72c5497bf3581439f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 4 Oct 2017 11:20:39 +0300 Subject: [PATCH 05/23] Allow to specify more than one api version for services. --- .../AbpApiVersioningOptionsExtensions.cs | 51 +++++++++++++++---- .../AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs | 1 + .../Mvc/AbpControllerAssemblySetting.cs | 8 ++- .../AbpIdentityHttpApiHostModule.cs | 8 ++- .../Abp/Identity/AbpIdentityHttpApiModule.cs | 7 +-- .../Volo/Abp/Identity/FixtureController.cs | 31 +++++++++++ 6 files changed, 89 insertions(+), 17 deletions(-) create mode 100644 src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs diff --git a/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs b/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs index d72ec24d95..689b378255 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs @@ -1,5 +1,5 @@ -using System.Reflection; -using Microsoft.AspNetCore.Mvc; +using System.Linq; +using System.Reflection; using Microsoft.AspNetCore.Mvc.Versioning; using Microsoft.AspNetCore.Mvc.Versioning.Conventions; using Volo.Abp.AspNetCore.Mvc; @@ -8,26 +8,57 @@ namespace Microsoft.Extensions.DependencyInjection { public static class AbpApiVersioningOptionsExtensions { - public static void AddAbpModules(this ApiVersioningOptions options, IServiceCollection services) + public static void ConfigureAbpModules(this ApiVersioningOptions options, IServiceCollection services) { + //TODO: Use new builder will be released with Api Versioning 2.1 instead of reflection! + services.Configure(op => { + //TODO: Configuring api version should be done directly inside ConfigureAbpModules, + //TODO: not in a callback that will be called by MVC later! For that, we immediately need to controllerAssemblySettings + foreach (var setting in op.AppServiceControllers.ControllerAssemblySettings) { - foreach (var controllerType in setting.ControllerTypes) + if (setting.ApiVersionConfigurer == null) + { + ConfigureApiVersionsByConvention(options, setting); + } + else { - var controllerBuilder = typeof(ApiVersionConventionBuilder) - .GetMethod(nameof(ApiVersionConventionBuilder.Controller), BindingFlags.Instance | BindingFlags.Public) - .MakeGenericMethod(controllerType) - .Invoke(options.Conventions, null); + setting.ApiVersionConfigurer.Invoke(options); + } + } + }); + } + private static void ConfigureApiVersionsByConvention(ApiVersioningOptions options, AbpControllerAssemblySetting setting) + { + foreach (var controllerType in setting.ControllerTypes) + { + var controllerBuilder = typeof(ApiVersionConventionBuilder) + .GetMethod(nameof(ApiVersionConventionBuilder.Controller), + BindingFlags.Instance | BindingFlags.Public) + .MakeGenericMethod(controllerType) + .Invoke(options.Conventions, null); + + if (setting.ApiVersions.Any()) + { + foreach (var apiVersion in setting.ApiVersions) + { typeof(ControllerApiVersionConventionBuilder<>) .MakeGenericType(controllerType) .GetMethod("HasApiVersion") - .Invoke(controllerBuilder, new object[] { setting.ApiVersion }); + .Invoke(controllerBuilder, new object[] {apiVersion}); } } - }); + else + { + typeof(ControllerApiVersionConventionBuilder<>) + .MakeGenericType(controllerType) + .GetMethod("IsApiVersionNeutral") + .Invoke(controllerBuilder, null); + } + } } } } diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs index 781824846a..7d2bb24341 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs @@ -2,6 +2,7 @@ { public class AbpAspNetCoreMvcOptions { + //TODO: Rename to ConventionalControllers public AppServiceControllerOptions AppServiceControllers { get; } public AbpAspNetCoreMvcOptions() diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs index a99b87855e..ffe510eaaa 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs @@ -5,6 +5,7 @@ using System.Reflection; using JetBrains.Annotations; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.AspNetCore.Mvc.Versioning; using Volo.Abp.Application.Services; using Volo.Abp.Reflection; @@ -42,7 +43,10 @@ namespace Volo.Abp.AspNetCore.Mvc [CanBeNull] public Func UrlActionNameNormalizer { get; set; } - public ApiVersion ApiVersion { get; set; } + public List ApiVersions { get; set; } + + public Action ApiVersionConfigurer { get; set; } + public AbpControllerAssemblySetting([NotNull] Assembly assembly, [NotNull] string rootPath) { @@ -53,7 +57,7 @@ namespace Volo.Abp.AspNetCore.Mvc ControllerTypes = new HashSet(); - ApiVersion = new ApiVersion(1, 0); + ApiVersions = new List(); } public void Initialize() diff --git a/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs b/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs index 01b14f1216..c171b4fa93 100644 --- a/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs +++ b/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Reflection; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; @@ -32,6 +33,8 @@ namespace Volo.Abp.Identity.HttpApi.Host var hostingEnvironment = services.GetSingletonInstance(); //TOD: Move to BuildConfiguration method var configuration = BuildConfiguration(hostingEnvironment); + services.AddOptions(); //TODO: Remove later, for test purposes + services.Configure(configuration); services.Configure(options => @@ -55,7 +58,8 @@ namespace Volo.Abp.Identity.HttpApi.Host services.AddApiVersioning(o => { o.ReportApiVersions = true; - o.Conventions.Controller().IsApiVersionNeutral(); + o.Conventions.Controller().IsApiVersionNeutral(); //TODO: This should be inside the framework! + //o.Conventions.Controller().Action((MethodInfo)null).MapToApiVersion(new ApiVersion(1,1),).IsApiVersionNeutral(); //o.Conventions.Controller().HasApiVersion(new ApiVersion(3, 0)); //We can do that based on controller's AbpApiVersion attribute! o.AssumeDefaultVersionWhenUnspecified = true; o.DefaultApiVersion = new ApiVersion(3, 0); //Default: 1.0 //We can not rely on that, application should do. @@ -65,7 +69,7 @@ namespace Volo.Abp.Identity.HttpApi.Host //o.Conventions.Controller().HasApiVersion(2, 0); //o.Conventions.Controller().IsApiVersionNeutral(); - o.AddAbpModules(services); + o.ConfigureAbpModules(services); }); services.AddSwaggerGen( diff --git a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs index f452e08507..195ce009b7 100644 --- a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs +++ b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs @@ -1,4 +1,5 @@ using System; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.Modularity; @@ -18,11 +19,11 @@ namespace Volo.Abp.Identity { opts.RootPath = "identity"; opts.UrlControllerNameNormalizer = context => context.ControllerName.RemovePreFix("Identity"); + opts.ApiVersions.Add(new ApiVersion(2, 0)); }); - }); - //TODO: Allow to use Api Versioning's API to explicitly configure versioning for app services and other controllers, - //TODO: rather than implicitly doing it via AppServiceControllers.Create call above! + options.AppServiceControllers.Create(typeof(AbpIdentityHttpApiModule).Assembly); + }); } } } \ No newline at end of file diff --git a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs new file mode 100644 index 0000000000..cd12153418 --- /dev/null +++ b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Application.Services; +using Volo.Abp.AspNetCore.Mvc; + +namespace Volo.Abp.Identity +{ + //TODO: This is just a test controller and will be removed lster + [Route("api/identity/fixture")] + [ApiVersion("2.0", Deprecated = true)] + [ApiVersion("3.0")] + public class FixtureController : AbpController, IRemoteService + { + [HttpGet] + public int Get() + { + return 42; + } + + [HttpGet, MapToApiVersion("3.0")] + public int Get3() + { + return 42; + } + + [HttpPost] + public int Post() + { + return 42; + } + } +} \ No newline at end of file From beb9abb61816f3fb35da000e7d67e9a303a7061f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 4 Oct 2017 11:24:45 +0300 Subject: [PATCH 06/23] Fix FixtureController --- .../Volo/Abp/Identity/FixtureController.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs index cd12153418..eca154f6ab 100644 --- a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs +++ b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs @@ -4,10 +4,10 @@ using Volo.Abp.AspNetCore.Mvc; namespace Volo.Abp.Identity { - //TODO: This is just a test controller and will be removed lster - [Route("api/identity/fixture")] - [ApiVersion("2.0", Deprecated = true)] + //TODO: This is just a test controller and will be removed later [ApiVersion("3.0")] + [ApiVersion("2.0", Deprecated = true)] + [Route("api/v{api-version:apiVersion}/identity/fixture")] public class FixtureController : AbpController, IRemoteService { [HttpGet] @@ -16,11 +16,11 @@ namespace Volo.Abp.Identity return 42; } - [HttpGet, MapToApiVersion("3.0")] - public int Get3() - { - return 42; - } + //[HttpGet, MapToApiVersion("3.0")] + //public int Get3() + //{ + // return 42; + //} [HttpPost] public int Post() From d655a65d5e2b2d98b8fe24c019a34663efbb5c9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 4 Oct 2017 11:35:13 +0300 Subject: [PATCH 07/23] Add IsApiVersionNeutral only if not defined ApiVersionAttribute. --- .../AbpApiVersioningOptionsExtensions.cs | 12 ++++++++---- .../Volo/Abp/Identity/FixtureController.cs | 16 ++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs b/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs index 689b378255..372b216583 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Reflection; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Versioning; using Microsoft.AspNetCore.Mvc.Versioning.Conventions; using Volo.Abp.AspNetCore.Mvc; @@ -53,10 +54,13 @@ namespace Microsoft.Extensions.DependencyInjection } else { - typeof(ControllerApiVersionConventionBuilder<>) - .MakeGenericType(controllerType) - .GetMethod("IsApiVersionNeutral") - .Invoke(controllerBuilder, null); + if (!controllerType.IsDefined(typeof(ApiVersionAttribute), true)) + { + typeof(ControllerApiVersionConventionBuilder<>) + .MakeGenericType(controllerType) + .GetMethod("IsApiVersionNeutral") + .Invoke(controllerBuilder, null); + } } } } diff --git a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs index eca154f6ab..74d831e65e 100644 --- a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs +++ b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs @@ -10,22 +10,22 @@ namespace Volo.Abp.Identity [Route("api/v{api-version:apiVersion}/identity/fixture")] public class FixtureController : AbpController, IRemoteService { - [HttpGet] + [HttpGet, MapToApiVersion("2.0")] public int Get() { - return 42; + return 41; } - //[HttpGet, MapToApiVersion("3.0")] - //public int Get3() - //{ - // return 42; - //} + [HttpGet, MapToApiVersion("3.0")] + public int Get3() + { + return 42; + } [HttpPost] public int Post() { - return 42; + return 43; } } } \ No newline at end of file From e2496b87cce03af9121e978c103de049fef2bd20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 4 Oct 2017 14:00:43 +0300 Subject: [PATCH 08/23] Getting client version info from core layer #103. --- .../HttpContextRequestedApiVersion.cs | 19 +++++++++++++++++++ .../Volo/Abp/Identity/FixtureController.cs | 16 ++++++++++++---- .../Abp/ApiVersioning/IRequestedApiVersion.cs | 7 +++++++ 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Versioning/HttpContextRequestedApiVersion.cs create mode 100644 src/Volo.Abp/Volo/Abp/ApiVersioning/IRequestedApiVersion.cs diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Versioning/HttpContextRequestedApiVersion.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Versioning/HttpContextRequestedApiVersion.cs new file mode 100644 index 0000000000..911c6e746e --- /dev/null +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Versioning/HttpContextRequestedApiVersion.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.ApiVersioning; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Mvc.Versioning +{ + public class HttpContextRequestedApiVersion : IRequestedApiVersion, ITransientDependency + { + public string Current => _httpContextAccessor.HttpContext?.GetRequestedApiVersion().ToString(); + + private readonly IHttpContextAccessor _httpContextAccessor; + + public HttpContextRequestedApiVersion(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs index 74d831e65e..7826bc5e7b 100644 --- a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs +++ b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Mvc; +using Volo.Abp.ApiVersioning; using Volo.Abp.Application.Services; using Volo.Abp.AspNetCore.Mvc; @@ -10,16 +11,23 @@ namespace Volo.Abp.Identity [Route("api/v{api-version:apiVersion}/identity/fixture")] public class FixtureController : AbpController, IRemoteService { + private readonly IRequestedApiVersion _requestedApiVersion; + + public FixtureController(IRequestedApiVersion requestedApiVersion) + { + _requestedApiVersion = requestedApiVersion; + } + [HttpGet, MapToApiVersion("2.0")] - public int Get() + public string Get() { - return 41; + return 41 + " - " + _requestedApiVersion.Current; } [HttpGet, MapToApiVersion("3.0")] - public int Get3() + public string Get3() { - return 42; + return 42 + " - " + _requestedApiVersion.Current; } [HttpPost] diff --git a/src/Volo.Abp/Volo/Abp/ApiVersioning/IRequestedApiVersion.cs b/src/Volo.Abp/Volo/Abp/ApiVersioning/IRequestedApiVersion.cs new file mode 100644 index 0000000000..3d8a31c694 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/ApiVersioning/IRequestedApiVersion.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.ApiVersioning +{ + public interface IRequestedApiVersion + { + string Current { get; } + } +} From 1df5821fb62cf384464fe5e45c0d8606668692b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 4 Oct 2017 15:52:50 +0300 Subject: [PATCH 09/23] API Versioning improvements. --- .../AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs | 17 ++++++++++++ .../Mvc/AbpControllerAssemblySetting.cs | 3 +-- .../AbpApiDefinitionController.cs | 5 ++-- .../AspNetCoreApiDescriptionModelProvider.cs | 15 +++++------ .../DynamicProxying/ApiDescriptionCache.cs | 2 +- .../Modeling/ActionApiDescriptionModel.cs | 2 +- .../Modeling/ApiDescriptionModelOptions.cs | 26 +++++++++++++++++++ .../Modeling/ControllerApiDescriptionModel.cs | 4 ++- .../Modeling/ModuleApiDescriptionModel.cs | 5 ++-- .../AbpIdentityHttpApiHostModule.cs | 11 +------- .../Controllers/HomeController.cs | 1 - 11 files changed, 62 insertions(+), 29 deletions(-) create mode 100644 src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ApiDescriptionModelOptions.cs diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs index 072e523877..088936b69d 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs @@ -12,10 +12,12 @@ using Volo.Abp.DependencyInjection; using Volo.Abp.Modularity; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.ViewComponents; using Microsoft.Extensions.DependencyInjection.Extensions; using Volo.Abp.Http; +using Volo.Abp.Http.Modeling; namespace Volo.Abp.AspNetCore.Mvc { @@ -41,6 +43,21 @@ namespace Volo.Abp.AspNetCore.Mvc ) ) ); + + services.Configure(options => + { + options.IgnoredInterfaces.AddIfNotContains(typeof(IAsyncActionFilter)); + options.IgnoredInterfaces.AddIfNotContains(typeof(IFilterMetadata)); + options.IgnoredInterfaces.AddIfNotContains(typeof(IActionFilter)); + }); + + services.Configure(options => + { + options.AppServiceControllers.Create(typeof(AbpAspNetCoreMvcModule).Assembly, o => + { + o.RootPath = "abp"; + }); + }); } public override void PostConfigureServices(IServiceCollection services) diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs index ffe510eaaa..be40f2d95f 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs @@ -46,8 +46,7 @@ namespace Volo.Abp.AspNetCore.Mvc public List ApiVersions { get; set; } public Action ApiVersionConfigurer { get; set; } - - + public AbpControllerAssemblySetting([NotNull] Assembly assembly, [NotNull] string rootPath) { Check.NotNull(assembly, rootPath); diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpApiDefinitionController.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpApiDefinitionController.cs index 493eaef389..19db653dc8 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpApiDefinitionController.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpApiDefinitionController.cs @@ -1,10 +1,11 @@ using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Application.Services; using Volo.Abp.Http.Modeling; namespace Volo.Abp.AspNetCore.Mvc.ApiExploring { - [Route("api/abp/api-description")] - public class AbpApiDefinitionController : AbpController + [Route("api/abp/api-definition")] + public class AbpApiDefinitionController : AbpController, IRemoteService { private readonly IApiDescriptionModelProvider _modelProvider; diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs index 3a2fdba255..f9802f2103 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; @@ -9,7 +10,6 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; -using Volo.Abp.Application.Services; using Volo.Abp.AspNetCore.Mvc.Utils; using Volo.Abp.DependencyInjection; using Volo.Abp.Http.Modeling; @@ -22,13 +22,16 @@ namespace Volo.Abp.AspNetCore.Mvc private readonly IApiDescriptionGroupCollectionProvider _descriptionProvider; private readonly AbpAspNetCoreMvcOptions _options; + private readonly ApiDescriptionModelOptions _modelOptions; public AspNetCoreApiDescriptionModelProvider( IApiDescriptionGroupCollectionProvider descriptionProvider, - IOptions options) + IOptions options, + IOptions modelOptions) { _descriptionProvider = descriptionProvider; _options = options.Value; + _modelOptions = modelOptions.Value; Logger = NullLogger.Instance; } @@ -59,7 +62,7 @@ namespace Volo.Abp.AspNetCore.Mvc var moduleModel = model.GetOrAddModule(GetRootPath(controllerType)); - var controllerModel = moduleModel.GetOrAddController(GetControllerName(apiDescription), controllerType); + var controllerModel = moduleModel.GetOrAddController(controllerType.FullName, controllerType, _modelOptions.IgnoredInterfaces); var method = apiDescription.ActionDescriptor.GetMethodInfo(); @@ -80,12 +83,6 @@ namespace Volo.Abp.AspNetCore.Mvc AddParameterDescriptionsToModel(actionModel, method, apiDescription); } - private static string GetControllerName(ApiDescription apiDescription) - { - return apiDescription.GroupName?.RemovePostFix(ApplicationService.CommonPostfixes) - ?? apiDescription.ActionDescriptor.AsControllerActionDescriptor().ControllerName; - } - private static string GetUniqueActionName(MethodInfo method) { var methodNameBuilder = new StringBuilder(method.Name); diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionCache.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionCache.cs index 4fc370a2e2..4476ef90cd 100644 --- a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionCache.cs +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionCache.cs @@ -42,7 +42,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying { using (var client = _httpClientFactory.Create()) { - var response = await client.GetAsync(baseUrl + "api/abp/api-description"); + var response = await client.GetAsync(baseUrl + "api/abp/api-definition"); if (!response.IsSuccessStatusCode) { throw new AbpException("Remote service returns error!"); diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs index fe91a3ddf2..75c22621c3 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs @@ -28,7 +28,7 @@ namespace Volo.Abp.Http.Modeling } - public static ActionApiDescriptionModel Create(MethodInfo method, string uniqueName, string url, string httpMethod = null) + public static ActionApiDescriptionModel Create(MethodInfo method, string uniqueName, string url, string httpMethod) { return new ActionApiDescriptionModel { diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ApiDescriptionModelOptions.cs b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ApiDescriptionModelOptions.cs new file mode 100644 index 0000000000..18b335b115 --- /dev/null +++ b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ApiDescriptionModelOptions.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using Volo.Abp.Application.Services; +using Volo.Abp.Aspects; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Http.Modeling +{ + public class ApiDescriptionModelOptions + { + public HashSet IgnoredInterfaces { get; } + + public ApiDescriptionModelOptions() + { + IgnoredInterfaces = new HashSet + { + typeof(IApplicationService), + typeof(IRemoteService), + typeof(ITransientDependency), + typeof(ISingletonDependency), + typeof(IDisposable), + typeof(IAvoidDuplicateCrossCuttingConcerns) + }; + } + } +} diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs index 51f03661f4..ca9dbb9415 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; namespace Volo.Abp.Http.Modeling { @@ -20,7 +21,7 @@ namespace Volo.Abp.Http.Modeling } - public static ControllerApiDescriptionModel Create(string controllerName, Type type) + public static ControllerApiDescriptionModel Create(string controllerName, Type type, [CanBeNull] HashSet ignoredInterfaces = null) { return new ControllerApiDescriptionModel { @@ -29,6 +30,7 @@ namespace Volo.Abp.Http.Modeling Actions = new Dictionary(), Interfaces = type .GetInterfaces() + .WhereIf(ignoredInterfaces != null, i => !i.IsGenericType && !ignoredInterfaces.Contains(i)) .Select(ControllerInterfaceApiDescriptionModel.Create) .ToList() }; diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ModuleApiDescriptionModel.cs b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ModuleApiDescriptionModel.cs index 5f776d5e4a..e776818508 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ModuleApiDescriptionModel.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ModuleApiDescriptionModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; namespace Volo.Abp.Http.Modeling { @@ -40,9 +41,9 @@ namespace Volo.Abp.Http.Modeling return Controllers[controller.ControllerName] = controller; } - public ControllerApiDescriptionModel GetOrAddController(string name, Type type) + public ControllerApiDescriptionModel GetOrAddController(string name, Type type, [CanBeNull] HashSet ignoredInterfaces = null) { - return Controllers.GetOrAdd(name, () => ControllerApiDescriptionModel.Create(name, type)); + return Controllers.GetOrAdd(name, () => ControllerApiDescriptionModel.Create(name, type, ignoredInterfaces)); } public ModuleApiDescriptionModel CreateSubModel(string[] controllers, string[] actions) diff --git a/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs b/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs index c171b4fa93..503cd2189f 100644 --- a/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs +++ b/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs @@ -1,14 +1,7 @@ -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.AspNetCore.Mvc.ApplicationModels; -using Microsoft.AspNetCore.Mvc.RazorPages.Internal; -using Microsoft.AspNetCore.Mvc.Versioning; -using Microsoft.AspNetCore.Mvc.Versioning.Conventions; using Microsoft.EntityFrameworkCore; using Microsoft.Examples; using Microsoft.Extensions.Configuration; @@ -16,7 +9,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Swashbuckle.AspNetCore.Swagger; using Volo.Abp.AspNetCore.Modularity; -using Volo.Abp.AspNetCore.Mvc.ApiExploring; using Volo.Abp.Autofac; using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; @@ -58,7 +50,6 @@ namespace Volo.Abp.Identity.HttpApi.Host services.AddApiVersioning(o => { o.ReportApiVersions = true; - o.Conventions.Controller().IsApiVersionNeutral(); //TODO: This should be inside the framework! //o.Conventions.Controller().Action((MethodInfo)null).MapToApiVersion(new ApiVersion(1,1),).IsApiVersionNeutral(); //o.Conventions.Controller().HasApiVersion(new ApiVersion(3, 0)); //We can do that based on controller's AbpApiVersion attribute! o.AssumeDefaultVersionWhenUnspecified = true; diff --git a/src/Volo.Abp.Identity.HttpApi.Host/Controllers/HomeController.cs b/src/Volo.Abp.Identity.HttpApi.Host/Controllers/HomeController.cs index 2a8c3aed0b..3b61831e3f 100644 --- a/src/Volo.Abp.Identity.HttpApi.Host/Controllers/HomeController.cs +++ b/src/Volo.Abp.Identity.HttpApi.Host/Controllers/HomeController.cs @@ -6,7 +6,6 @@ namespace Volo.Abp.Identity.HttpApi.Host.Controllers public class HomeController : AbpController { [HttpPost] - [Route("/api/v1/users/create")] public IActionResult Index() { return Redirect("/swagger"); From f0563bd00b9a027bcd1150c4ac42883b6fd9d62e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 4 Oct 2017 16:24:15 +0300 Subject: [PATCH 10/23] API improvements. --- .../AspNetCore/Mvc/AbpAppServiceConvention.cs | 4 +- .../AspNetCoreApiDescriptionModelProvider.cs | 50 +++++++++++++------ .../Mvc/UrlControllerNameNormalizerContext.cs | 13 +---- .../JQuery/JQueryProxyScriptGenerator.cs | 2 +- 4 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs index 0952934df6..dd261936e2 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs @@ -311,9 +311,7 @@ namespace Volo.Abp.AspNetCore.Mvc return configuration.UrlControllerNameNormalizer( new UrlControllerNameNormalizerContext( rootPath, - controllerName, - action, - httpMethod + controllerName ) ); } diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs index f9802f2103..e41fa04806 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs @@ -1,8 +1,8 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; +using JetBrains.Annotations; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.ApiExplorer; @@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; +using Volo.Abp.Application.Services; using Volo.Abp.AspNetCore.Mvc.Utils; using Volo.Abp.DependencyInjection; using Volo.Abp.Http.Modeling; @@ -38,6 +39,8 @@ namespace Volo.Abp.AspNetCore.Mvc public ApplicationApiDescriptionModel CreateApiModel() { + //TODO: Can cache the model? + var model = ApplicationApiDescriptionModel.Create(); foreach (var descriptionGroupItem in _descriptionProvider.ApiDescriptionGroups.Items) @@ -59,10 +62,11 @@ namespace Volo.Abp.AspNetCore.Mvc private void AddApiDescriptionToModel(ApiDescription apiDescription, ApplicationApiDescriptionModel model) { var controllerType = apiDescription.ActionDescriptor.AsControllerActionDescriptor().ControllerTypeInfo.AsType(); + var setting = FindSetting(controllerType); - var moduleModel = model.GetOrAddModule(GetRootPath(controllerType)); + var moduleModel = model.GetOrAddModule(GetRootPath(controllerType, setting)); - var controllerModel = moduleModel.GetOrAddController(controllerType.FullName, controllerType, _modelOptions.IgnoredInterfaces); + var controllerModel = moduleModel.GetOrAddController(CalculateControllerName(controllerType, setting), controllerType, _modelOptions.IgnoredInterfaces); var method = apiDescription.ActionDescriptor.GetMethodInfo(); @@ -83,6 +87,18 @@ namespace Volo.Abp.AspNetCore.Mvc AddParameterDescriptionsToModel(actionModel, method, apiDescription); } + private static string CalculateControllerName(Type controllerType, AbpControllerAssemblySetting setting) + { + var controllerName = controllerType.Name.RemovePostFix("Controller").RemovePostFix(ApplicationService.CommonPostfixes); + + if (setting?.UrlControllerNameNormalizer != null) + { + controllerName = setting.UrlControllerNameNormalizer(new UrlControllerNameNormalizerContext(setting.RootPath, controllerName)); + } + + return controllerName; + } + private static string GetUniqueActionName(MethodInfo method) { var methodNameBuilder = new StringBuilder(method.Name); @@ -152,19 +168,11 @@ namespace Volo.Abp.AspNetCore.Mvc return modelNameProvider.Name; } - private string GetRootPath(Type controllerType) + private static string GetRootPath([NotNull] Type controllerType, [CanBeNull] AbpControllerAssemblySetting setting) { - if (controllerType == null) + if (setting != null) { - return ModuleApiDescriptionModel.DefaultRootPath; - } - - foreach (var controllerSetting in _options.AppServiceControllers.ControllerAssemblySettings) - { - if(controllerSetting.ControllerTypes.Contains(controllerType)) - { - return controllerSetting.RootPath; - } + return setting.RootPath; } var areaAttr = controllerType.GetCustomAttributes().OfType().FirstOrDefault(); @@ -175,5 +183,19 @@ namespace Volo.Abp.AspNetCore.Mvc return ModuleApiDescriptionModel.DefaultRootPath; } + + [CanBeNull] + private AbpControllerAssemblySetting FindSetting(Type controllerType) + { + foreach (var controllerSetting in _options.AppServiceControllers.ControllerAssemblySettings) + { + if (controllerSetting.ControllerTypes.Contains(controllerType)) + { + return controllerSetting; + } + } + + return null; + } } } diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/UrlControllerNameNormalizerContext.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/UrlControllerNameNormalizerContext.cs index 90bffe8df4..aad40fe4d7 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/UrlControllerNameNormalizerContext.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/UrlControllerNameNormalizerContext.cs @@ -1,24 +1,15 @@ -using Microsoft.AspNetCore.Mvc.ApplicationModels; - -namespace Volo.Abp.AspNetCore.Mvc +namespace Volo.Abp.AspNetCore.Mvc { - //TODO: Re-consider properties of this class. public class UrlControllerNameNormalizerContext { public string RootPath { get; } public string ControllerName { get; } - public ActionModel Action { get; } - - public string HttpMethod { get; } - - public UrlControllerNameNormalizerContext(string rootPath, string controllerName, ActionModel action, string httpMethod) + public UrlControllerNameNormalizerContext(string rootPath, string controllerName) { RootPath = rootPath; ControllerName = controllerName; - Action = action; - HttpMethod = httpMethod; } } } \ No newline at end of file diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs b/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs index 87dbba1bad..299a9855e4 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs @@ -74,7 +74,7 @@ namespace Volo.Abp.Http.ProxyScripting.Generators.JQuery var parameterList = ProxyScriptingJsFuncHelper.GenerateJsFuncParameterList(action, "ajaxParams"); script.AppendLine($" // action '{action.NameOnClass.ToCamelCase()}'"); - script.AppendLine($" abp.services.{module.RootPath.Replace("/", ".").ToCamelCase()}.{controller.ControllerName.ToCamelCase()}{ProxyScriptingJsFuncHelper.WrapWithBracketsOrWithDotPrefix(action.NameOnClass.ToCamelCase())} = function({parameterList}) {{"); + script.AppendLine($" abp.services.{module.RootPath.Replace("/", ".").ToCamelCase()}.{controller.ControllerName.ToCamelCase()}{ProxyScriptingJsFuncHelper.WrapWithBracketsOrWithDotPrefix(action.NameOnClass.RemovePostFix("Async").ToCamelCase())} = function({parameterList}) {{"); script.AppendLine(" return abp.ajax($.extend(true, {"); AddAjaxCallParameters(script, controller, action); From 0635e74ed69da8e7ff60ce78006c8961dd832ef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 4 Oct 2017 17:26:48 +0300 Subject: [PATCH 11/23] Revised API and script creation. --- .../AspNetCoreApiDescriptionModelProvider.cs | 7 ++-- .../DynamicProxying/ApiDescriptionFinder.cs | 2 +- .../Modeling/ActionApiDescriptionModel.cs | 9 ++--- .../Modeling/ControllerApiDescriptionModel.cs | 16 ++++----- .../Modeling/ModuleApiDescriptionModel.cs | 4 +-- .../JQuery/JQueryProxyScriptGenerator.cs | 4 +-- .../Generators/ProxyScriptingHelper.cs | 2 +- .../AbpIdentityHttpApiHostModule.cs | 18 ++++++++-- .../Controllers/HomeController.cs | 1 - .../VersioningTests/V1/CallsController.cs | 33 +++---------------- .../VersioningTests/V2/Calls2Controller.cs | 33 +++++++++++++++++++ 11 files changed, 73 insertions(+), 56 deletions(-) create mode 100644 src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V2/Calls2Controller.cs diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs index e41fa04806..8368b2e9d0 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs @@ -66,7 +66,7 @@ namespace Volo.Abp.AspNetCore.Mvc var moduleModel = model.GetOrAddModule(GetRootPath(controllerType, setting)); - var controllerModel = moduleModel.GetOrAddController(CalculateControllerName(controllerType, setting), controllerType, _modelOptions.IgnoredInterfaces); + var controllerModel = moduleModel.GetOrAddController(controllerType.FullName, CalculateControllerName(controllerType, setting), controllerType, _modelOptions.IgnoredInterfaces); var method = apiDescription.ActionDescriptor.GetMethodInfo(); @@ -77,9 +77,8 @@ namespace Volo.Abp.AspNetCore.Mvc return; } - var actionModel = controllerModel.AddAction(ActionApiDescriptionModel.Create( + var actionModel = controllerModel.AddAction(uniqueMethodName, ActionApiDescriptionModel.Create( method, - uniqueMethodName, apiDescription.RelativePath, apiDescription.HttpMethod )); @@ -101,7 +100,7 @@ namespace Volo.Abp.AspNetCore.Mvc private static string GetUniqueActionName(MethodInfo method) { - var methodNameBuilder = new StringBuilder(method.Name); + var methodNameBuilder = new StringBuilder(method.Name.RemovePostFix("Async")); var parameters = method.GetParameters(); if (parameters.Any()) diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs index b58e43e1ec..206b55c2a4 100644 --- a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs @@ -35,7 +35,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying foreach (var action in controller.Actions.Values) { - if (action.NameOnClass == method.Name && action.ParametersOnMethod.Count == methodParameters.Length) + if (action.Name == method.Name && action.ParametersOnMethod.Count == methodParameters.Length) { var found = true; diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs index 75c22621c3..9b93ea792c 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs @@ -9,9 +9,7 @@ namespace Volo.Abp.Http.Modeling [Serializable] public class ActionApiDescriptionModel { - public string UniqueName { get; set; } - - public string NameOnClass { get; set; } + public string Name { get; set; } public string HttpMethod { get; set; } @@ -28,12 +26,11 @@ namespace Volo.Abp.Http.Modeling } - public static ActionApiDescriptionModel Create(MethodInfo method, string uniqueName, string url, string httpMethod) + public static ActionApiDescriptionModel Create(MethodInfo method, string url, string httpMethod) { return new ActionApiDescriptionModel { - UniqueName = uniqueName, - NameOnClass = method.Name, + Name = method.Name, Url = url, HttpMethod = httpMethod, ReturnValue = ReturnValueApiDescriptionModel.Create(method.ReturnType), diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs index ca9dbb9415..9d7216d357 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs @@ -36,16 +36,16 @@ namespace Volo.Abp.Http.Modeling }; } - public ActionApiDescriptionModel AddAction(ActionApiDescriptionModel action) + public ActionApiDescriptionModel AddAction(string uniqueName, ActionApiDescriptionModel action) { - if (Actions.ContainsKey(action.UniqueName)) + if (Actions.ContainsKey(uniqueName)) { throw new AbpException( - $"Can not add more than one action with same name to the same controller. Controller: {ControllerName}, Action: {action.UniqueName}." - ); + $"Can not add more than one action with same name to the same controller. Controller: {ControllerName}, Action: {action.Name}." + ); } - return Actions[action.UniqueName] = action; + return Actions[uniqueName] = action; } public ControllerApiDescriptionModel CreateSubModel(string[] actions) @@ -58,11 +58,11 @@ namespace Volo.Abp.Http.Modeling Actions = new Dictionary() }; - foreach (var action in Actions.Values) + foreach (var action in Actions) { - if (actions == null || actions.Contains(action.UniqueName)) + if (actions == null || actions.Contains(action.Key)) { - subModel.AddAction(action); + subModel.AddAction(action.Key, action.Value); } } diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ModuleApiDescriptionModel.cs b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ModuleApiDescriptionModel.cs index e776818508..44967fa50c 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ModuleApiDescriptionModel.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ModuleApiDescriptionModel.cs @@ -41,9 +41,9 @@ namespace Volo.Abp.Http.Modeling return Controllers[controller.ControllerName] = controller; } - public ControllerApiDescriptionModel GetOrAddController(string name, Type type, [CanBeNull] HashSet ignoredInterfaces = null) + public ControllerApiDescriptionModel GetOrAddController(string uniqueName, string name, Type type, [CanBeNull] HashSet ignoredInterfaces = null) { - return Controllers.GetOrAdd(name, () => ControllerApiDescriptionModel.Create(name, type, ignoredInterfaces)); + return Controllers.GetOrAdd(uniqueName, () => ControllerApiDescriptionModel.Create(name, type, ignoredInterfaces)); } public ModuleApiDescriptionModel CreateSubModel(string[] controllers, string[] actions) diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs b/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs index 299a9855e4..8310d286ed 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs @@ -73,8 +73,8 @@ namespace Volo.Abp.Http.ProxyScripting.Generators.JQuery { var parameterList = ProxyScriptingJsFuncHelper.GenerateJsFuncParameterList(action, "ajaxParams"); - script.AppendLine($" // action '{action.NameOnClass.ToCamelCase()}'"); - script.AppendLine($" abp.services.{module.RootPath.Replace("/", ".").ToCamelCase()}.{controller.ControllerName.ToCamelCase()}{ProxyScriptingJsFuncHelper.WrapWithBracketsOrWithDotPrefix(action.NameOnClass.RemovePostFix("Async").ToCamelCase())} = function({parameterList}) {{"); + script.AppendLine($" // action '{action.Name.ToCamelCase()}'"); + script.AppendLine($" abp.services.{module.RootPath.Replace("/", ".").ToCamelCase()}.{controller.ControllerName.ToCamelCase()}{ProxyScriptingJsFuncHelper.WrapWithBracketsOrWithDotPrefix(action.Name.RemovePostFix("Async").ToCamelCase())} = function({parameterList}) {{"); script.AppendLine(" return abp.ajax($.extend(true, {"); AddAjaxCallParameters(script, controller, action); diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/ProxyScriptingHelper.cs b/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/ProxyScriptingHelper.cs index c962263bd8..64d1515574 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/ProxyScriptingHelper.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/ProxyScriptingHelper.cs @@ -47,7 +47,7 @@ namespace Volo.Abp.Http.ProxyScripting.Generators if (parameters.Length > 1) { throw new AbpException( - $"Only one complex type allowed as argument to a controller action that's binding source is 'Body'. But {action.UniqueName} ({action.Url}) contains more than one!" + $"Only one complex type allowed as argument to a controller action that's binding source is 'Body'. But {action.Name} ({action.Url}) contains more than one!" ); } diff --git a/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs b/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs index 503cd2189f..bfa67da872 100644 --- a/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs +++ b/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs @@ -9,10 +9,12 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Swashbuckle.AspNetCore.Swagger; using Volo.Abp.AspNetCore.Modularity; +using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.Autofac; using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.Identity.EntityFrameworkCore; +using Volo.Abp.Identity.HttpApi.Host.VersioningTests.V1; using Volo.Abp.Modularity; namespace Volo.Abp.Identity.HttpApi.Host @@ -25,8 +27,6 @@ namespace Volo.Abp.Identity.HttpApi.Host var hostingEnvironment = services.GetSingletonInstance(); //TOD: Move to BuildConfiguration method var configuration = BuildConfiguration(hostingEnvironment); - services.AddOptions(); //TODO: Remove later, for test purposes - services.Configure(configuration); services.Configure(options => @@ -88,6 +88,20 @@ namespace Volo.Abp.Identity.HttpApi.Host //options.IncludeXmlComments(XmlCommentsFilePath); //TODO: Add XML comments! }); + services.Configure(options => + { + options.AppServiceControllers.Create(typeof(AbpIdentityHttpApiHostModule).Assembly, o => + { + o.TypePredicate = t => t == typeof(CallsController); + }); + + options.AppServiceControllers.Create(typeof(AbpIdentityHttpApiHostModule).Assembly, o => + { + o.TypePredicate = t => t == typeof(Host.VersioningTests.V2.CallsController); + o.RootPath = "app/compat"; + }); + }); + services.AddAssemblyOf(); } diff --git a/src/Volo.Abp.Identity.HttpApi.Host/Controllers/HomeController.cs b/src/Volo.Abp.Identity.HttpApi.Host/Controllers/HomeController.cs index 3b61831e3f..34283e4d36 100644 --- a/src/Volo.Abp.Identity.HttpApi.Host/Controllers/HomeController.cs +++ b/src/Volo.Abp.Identity.HttpApi.Host/Controllers/HomeController.cs @@ -5,7 +5,6 @@ namespace Volo.Abp.Identity.HttpApi.Host.Controllers { public class HomeController : AbpController { - [HttpPost] public IActionResult Index() { return Redirect("/swagger"); diff --git a/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V1/CallsController.cs b/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V1/CallsController.cs index 79073d8ba7..5837df2e2b 100644 --- a/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V1/CallsController.cs +++ b/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V1/CallsController.cs @@ -1,16 +1,16 @@ using System.Collections.Generic; -using System.Linq; using Microsoft.AspNetCore.Mvc; using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; using Volo.Abp.AspNetCore.Mvc; namespace Volo.Abp.Identity.HttpApi.Host.VersioningTests.V1 { [ApiVersion("1.0")] [Route("api/v{api-version:apiVersion}/calls")] - public class CallsController : AbpController + public class CallsController : AbpController, IRemoteService { - private static List _calls = new List + private static readonly List Calls = new List { new CallDto {Id = 1, Number = "123456"}, new CallDto { Id = 2, Number = "123457" } @@ -19,7 +19,7 @@ namespace Volo.Abp.Identity.HttpApi.Host.VersioningTests.V1 [HttpGet] public List GetList() { - return _calls; + return Calls; } } @@ -27,29 +27,4 @@ namespace Volo.Abp.Identity.HttpApi.Host.VersioningTests.V1 { public string Number { get; set; } } - - [ApiVersion("2.0")] - [Route("api/v{api-version:apiVersion}/calls")] - [ControllerName("Calls")] - public class Calls2Controller : AbpController - { - private static List _calls = new List - { - new CallDto {Id = 1, Number = "123456000"}, - new CallDto { Id = 2, Number = "123457000" } - }; - - [HttpGet] - public List GetList() - { - return _calls; - } - - [HttpGet] - [Route("by-filter")] - public List GetList(string num) - { - return _calls.Where(c => c.Number.Contains(num)).ToList(); - } - } } diff --git a/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V2/Calls2Controller.cs b/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V2/Calls2Controller.cs new file mode 100644 index 0000000000..4a7b908782 --- /dev/null +++ b/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V2/Calls2Controller.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Application.Services; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.Identity.HttpApi.Host.VersioningTests.V1; + +namespace Volo.Abp.Identity.HttpApi.Host.VersioningTests.V2 +{ + [ApiVersion("2.0")] + [Route("api/v{api-version:apiVersion}/calls")] + public class CallsController : AbpController, IRemoteService + { + private static List _calls = new List + { + new CallDto {Id = 1, Number = "123456000"}, + new CallDto { Id = 2, Number = "123457000" } + }; + + [HttpGet] + public List GetList() + { + return _calls; + } + + [HttpGet] + [Route("by-filter")] + public List GetList(string num) + { + return _calls.Where(c => c.Number.Contains(num)).ToList(); + } + } +} \ No newline at end of file From 7cb0a887e54feb2fd435dba15d0490d0c240ced9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 5 Oct 2017 13:48:12 +0300 Subject: [PATCH 12/23] Improvements on versioning. --- .../AspNetCoreApiDescriptionModelProvider.cs | 2 +- .../DynamicProxying/ApiDescriptionFinder.cs | 4 +-- .../DynamicHttpProxyInterceptor.cs | 26 +++++++++++++------ .../DynamicProxying/IApiDescriptionFinder.cs | 2 +- .../Http/Client/RemoteServiceConfiguration.cs | 2 ++ .../Modeling/ActionApiDescriptionModel.cs | 5 ++++ .../Modeling/ControllerApiDescriptionModel.cs | 5 ++++ 7 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs index 8368b2e9d0..594850d10c 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs @@ -100,7 +100,7 @@ namespace Volo.Abp.AspNetCore.Mvc private static string GetUniqueActionName(MethodInfo method) { - var methodNameBuilder = new StringBuilder(method.Name.RemovePostFix("Async")); + var methodNameBuilder = new StringBuilder(method.Name); var parameters = method.GetParameters(); if (parameters.Any()) diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs index 206b55c2a4..34df0deecb 100644 --- a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiDescriptionFinder.cs @@ -16,9 +16,9 @@ namespace Volo.Abp.Http.Client.DynamicProxying _descriptionCache = descriptionCache; } - public async Task FindActionAsync(RemoteServiceConfiguration proxyConfig, Type serviceType, MethodInfo method) + public async Task FindActionAsync(string baseUrl, Type serviceType, MethodInfo method) { - var apiDescription = await _descriptionCache.GetAsync(proxyConfig.BaseUrl); + var apiDescription = await _descriptionCache.GetAsync(baseUrl); //TODO: Cache finding? diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs index fbd533dbba..db9b4e779a 100644 --- a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs @@ -94,9 +94,11 @@ namespace Volo.Abp.Http.Client.DynamicProxying { using (var client = _httpClientFactory.Create()) { - var proxyConfig = GetProxyConfig(); - var action = await _apiDescriptionFinder.FindActionAsync(proxyConfig, typeof(TService), invocation.Method); - var url = proxyConfig.BaseUrl + UrlBuilder.GenerateUrlWithParameters(action, invocation.ArgumentsDictionary); + var baseUrl = GetBaseUrl(); + var version = GetVersion(); //TODO: Add version to the request (querystring, media type, path value or custom header!) + + var action = await _apiDescriptionFinder.FindActionAsync(baseUrl, typeof(TService), invocation.Method); + var url = baseUrl + UrlBuilder.GenerateUrlWithParameters(action, invocation.ArgumentsDictionary); var requestMessage = new HttpRequestMessage(action.GetHttpMethod(), url) { @@ -128,16 +130,24 @@ namespace Volo.Abp.Http.Client.DynamicProxying } } - private RemoteServiceConfiguration GetProxyConfig() + private string GetBaseUrl() { var clientConfig = _clientOptions.HttpClientProxies.GetOrDefault(typeof(TService)) - ?? throw new AbpException($"Could not get DynamicHttpClientProxyConfig for {typeof(TService).FullName}."); + ?? throw new AbpException($"Could not get DynamicHttpClientProxyConfig for {typeof(TService).FullName}."); - return _remoteServiceOptions.RemoteServices.GetOrDefault(clientConfig.RemoteServiceName) - ?? _remoteServiceOptions.RemoteServices.Default - ?? throw new AbpException($"Could not get DynamicHttpClientProxyConfig for {typeof(TService).FullName}."); + return _remoteServiceOptions.RemoteServices.GetOrDefault(clientConfig.RemoteServiceName)?.BaseUrl + ?? _remoteServiceOptions.RemoteServices.Default?.BaseUrl + ?? throw new AbpException($"Could not find Base URL for {typeof(TService).FullName}."); } + private string GetVersion() + { + var clientConfig = _clientOptions.HttpClientProxies.GetOrDefault(typeof(TService)) + ?? throw new AbpException($"Could not get DynamicHttpClientProxyConfig for {typeof(TService).FullName}."); + + return _remoteServiceOptions.RemoteServices.GetOrDefault(clientConfig.RemoteServiceName)?.Version + ?? _remoteServiceOptions.RemoteServices.Default?.Version; + } private async Task ThrowExceptionForResponseAsync(HttpResponseMessage response) { diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/IApiDescriptionFinder.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/IApiDescriptionFinder.cs index af34a505c9..ede988b4b9 100644 --- a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/IApiDescriptionFinder.cs +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/IApiDescriptionFinder.cs @@ -7,6 +7,6 @@ namespace Volo.Abp.Http.Client.DynamicProxying { public interface IApiDescriptionFinder { - Task FindActionAsync(RemoteServiceConfiguration proxyConfig, Type serviceType, MethodInfo invocationMethod); + Task FindActionAsync(string baseUrl, Type serviceType, MethodInfo invocationMethod); } } \ No newline at end of file diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/RemoteServiceConfiguration.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/RemoteServiceConfiguration.cs index 666b9b4d5d..bf2f30133b 100644 --- a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/RemoteServiceConfiguration.cs +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/RemoteServiceConfiguration.cs @@ -4,6 +4,8 @@ { public string BaseUrl { get; set; } + public string Version { get; set; } + public RemoteServiceConfiguration() { diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs index 9b93ea792c..ffed6f2184 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs @@ -52,5 +52,10 @@ namespace Volo.Abp.Http.Modeling { return HttpMethodHelper.ConvertToHttpMethod(HttpMethod); } + + public override string ToString() + { + return $"[ActionApiDescriptionModel {Name}]"; + } } } \ No newline at end of file diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs index 9d7216d357..2bd9a301ab 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs @@ -73,5 +73,10 @@ namespace Volo.Abp.Http.Modeling { return Interfaces.Any(i => i.TypeAsString == interfaceType.GetFullNameWithAssemblyName()); } + + public override string ToString() + { + return $"[ControllerApiDescriptionModel {ControllerName}]"; + } } } \ No newline at end of file From a5c8e82e2c9618c536e59705a20f6b74c61c445e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 5 Oct 2017 15:02:54 +0300 Subject: [PATCH 13/23] Created unit tests. --- Volo.Abp.sln | 11 +++- .../AbpApiVersioningOptionsExtensions.cs | 8 ++- .../HttpContextRequestedApiVersion.cs | 3 +- .../DynamicHttpProxyInterceptor.cs | 18 +++++-- .../Http/Client/RemoteServiceConfiguration.cs | 3 +- .../AbpIdentityHttpApiHostModule.cs | 2 +- src/Volo.Abp/Volo/Abp/AbpKernelModule.cs | 3 ++ .../ApiVersioning/NullRequestedApiVersion.cs | 14 +++++ .../AbpAspNetCoreMvcTestModule.cs | 3 +- .../Abp/AspNetCore/{App => Mvc}/Startup.cs | 2 +- ...Abp.AspNetCore.Mvc.Versioning.Tests.csproj | 51 +++++++++++++++++++ .../AbpAspNetCoreMvcVersioningTestModule.cs | 48 +++++++++++++++++ .../Mvc/Versioning/App/ITodoAppService.cs | 9 ++++ .../Mvc/Versioning/App/TodoAppService.cs | 24 +++++++++ .../AspNetCoreMvcVersioningTestBase.cs | 6 +++ .../Abp/AspNetCore/Mvc/Versioning/Startup.cs | 27 ++++++++++ .../Versioning/Test/TodoAppService_Tests.cs | 23 +++++++++ ...ppTestBase.cs => AbpAspNetCoreTestBase.cs} | 0 .../Volo/Abp/Http/AbpHttpTestBase.cs | 1 - .../Volo/Abp/Http/AbpHttpTestModule.cs | 2 +- 20 files changed, 241 insertions(+), 17 deletions(-) create mode 100644 src/Volo.Abp/Volo/Abp/ApiVersioning/NullRequestedApiVersion.cs rename test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/{App => Mvc}/AbpAspNetCoreMvcTestModule.cs (95%) rename test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/{App => Mvc}/Startup.cs (95%) create mode 100644 test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj create mode 100644 test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs create mode 100644 test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/ITodoAppService.cs create mode 100644 test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/TodoAppService.cs create mode 100644 test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AspNetCoreMvcVersioningTestBase.cs create mode 100644 test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Startup.cs create mode 100644 test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/TodoAppService_Tests.cs rename test/Volo.Abp.AspNetCore.Tests/Volo/Abp/AspNetCore/{AppTestBase.cs => AbpAspNetCoreTestBase.cs} (100%) diff --git a/Volo.Abp.sln b/Volo.Abp.sln index ac4f6f1738..6cbfed4ab1 100644 --- a/Volo.Abp.sln +++ b/Volo.Abp.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26730.12 +VisualStudioVersion = 15.0.26730.16 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}" EndProject @@ -142,7 +142,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.EntityFrameworkCor EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleConsoleDemo", "test\SimpleConsoleDemo\SimpleConsoleDemo.csproj", "{2B48CF90-DBDB-469F-941C-5B5AECEEACE0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EntityFrameworkCore.Tests.SecondContext", "test\Volo.Abp.EntityFrameworkCore.Tests.SecondContext\Volo.Abp.EntityFrameworkCore.Tests.SecondContext.csproj", "{127FC2BF-DC40-4370-B845-16088328264C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.EntityFrameworkCore.Tests.SecondContext", "test\Volo.Abp.EntityFrameworkCore.Tests.SecondContext\Volo.Abp.EntityFrameworkCore.Tests.SecondContext.csproj", "{127FC2BF-DC40-4370-B845-16088328264C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.Versioning.Tests", "test\Volo.Abp.AspNetCore.Mvc.Versioning.Tests\Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj", "{A8C8B76D-0869-4C11-AC55-DB9DD115788E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -366,6 +368,10 @@ Global {127FC2BF-DC40-4370-B845-16088328264C}.Debug|Any CPU.Build.0 = Debug|Any CPU {127FC2BF-DC40-4370-B845-16088328264C}.Release|Any CPU.ActiveCfg = Release|Any CPU {127FC2BF-DC40-4370-B845-16088328264C}.Release|Any CPU.Build.0 = Release|Any CPU + {A8C8B76D-0869-4C11-AC55-DB9DD115788E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8C8B76D-0869-4C11-AC55-DB9DD115788E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8C8B76D-0869-4C11-AC55-DB9DD115788E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8C8B76D-0869-4C11-AC55-DB9DD115788E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -434,6 +440,7 @@ Global {3AF7C7F5-6513-47D4-8DD0-6E1AF14568D8} = {37087D1B-3693-4E96-983D-A69F210BDE53} {2B48CF90-DBDB-469F-941C-5B5AECEEACE0} = {37087D1B-3693-4E96-983D-A69F210BDE53} {127FC2BF-DC40-4370-B845-16088328264C} = {37087D1B-3693-4E96-983D-A69F210BDE53} + {A8C8B76D-0869-4C11-AC55-DB9DD115788E} = {37087D1B-3693-4E96-983D-A69F210BDE53} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs b/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs index 372b216583..7137d7fe3d 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs @@ -3,19 +3,23 @@ using System.Reflection; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Versioning; using Microsoft.AspNetCore.Mvc.Versioning.Conventions; +using Volo.Abp.ApiVersioning; using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.Versioning; namespace Microsoft.Extensions.DependencyInjection { public static class AbpApiVersioningOptionsExtensions { - public static void ConfigureAbpModules(this ApiVersioningOptions options, IServiceCollection services) + public static void ConfigureAbp(this ApiVersioningOptions options, IServiceCollection services) { //TODO: Use new builder will be released with Api Versioning 2.1 instead of reflection! + services.AddTransient(); + services.Configure(op => { - //TODO: Configuring api version should be done directly inside ConfigureAbpModules, + //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 foreach (var setting in op.AppServiceControllers.ControllerAssemblySettings) diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Versioning/HttpContextRequestedApiVersion.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Versioning/HttpContextRequestedApiVersion.cs index 911c6e746e..5678ccfe31 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Versioning/HttpContextRequestedApiVersion.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Versioning/HttpContextRequestedApiVersion.cs @@ -1,11 +1,10 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Volo.Abp.ApiVersioning; -using Volo.Abp.DependencyInjection; namespace Volo.Abp.AspNetCore.Mvc.Versioning { - public class HttpContextRequestedApiVersion : IRequestedApiVersion, ITransientDependency + public class HttpContextRequestedApiVersion : IRequestedApiVersion { public string Current => _httpContextAccessor.HttpContext?.GetRequestedApiVersion().ToString(); diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs index db9b4e779a..81b88f59f4 100644 --- a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs @@ -56,10 +56,20 @@ namespace Volo.Abp.Http.Client.DynamicProxying } else { - invocation.ReturnValue = _jsonSerializer.Deserialize( - invocation.Method.ReturnType, - AsyncHelper.RunSync(() => MakeRequest(invocation)) - ); + var responseAsString = AsyncHelper.RunSync(() => MakeRequest(invocation)); + + //TODO: Think on that + if (TypeHelper.IsPrimitiveExtendedIncludingNullable(invocation.Method.ReturnType, true)) + { + invocation.ReturnValue = Convert.ChangeType(responseAsString, invocation.Method.ReturnType); + } + else + { + invocation.ReturnValue = _jsonSerializer.Deserialize( + invocation.Method.ReturnType, + responseAsString + ); + } } } diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/RemoteServiceConfiguration.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/RemoteServiceConfiguration.cs index bf2f30133b..200e549c3e 100644 --- a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/RemoteServiceConfiguration.cs +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/RemoteServiceConfiguration.cs @@ -11,9 +11,10 @@ } - public RemoteServiceConfiguration(string baseUrl) + public RemoteServiceConfiguration(string baseUrl, string version = null) { BaseUrl = baseUrl; + Version = version; } } } \ No newline at end of file diff --git a/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs b/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs index bfa67da872..b826251fa2 100644 --- a/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs +++ b/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs @@ -60,7 +60,7 @@ namespace Volo.Abp.Identity.HttpApi.Host //o.Conventions.Controller().HasApiVersion(2, 0); //o.Conventions.Controller().IsApiVersionNeutral(); - o.ConfigureAbpModules(services); + o.ConfigureAbp(services); }); services.AddSwaggerGen( diff --git a/src/Volo.Abp/Volo/Abp/AbpKernelModule.cs b/src/Volo.Abp/Volo/Abp/AbpKernelModule.cs index 49966bad54..ad65a649b1 100644 --- a/src/Volo.Abp/Volo/Abp/AbpKernelModule.cs +++ b/src/Volo.Abp/Volo/Abp/AbpKernelModule.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.ApiVersioning; using Volo.Abp.Modularity; using Volo.Abp.ObjectMapping; using Volo.Abp.Reflection; @@ -33,6 +34,8 @@ namespace Volo.Abp services.AddLogging(); services.AddAssemblyOf(); + + services.AddSingleton(NullRequestedApiVersion.Instance); services.Configure(options => { diff --git a/src/Volo.Abp/Volo/Abp/ApiVersioning/NullRequestedApiVersion.cs b/src/Volo.Abp/Volo/Abp/ApiVersioning/NullRequestedApiVersion.cs new file mode 100644 index 0000000000..721fa91815 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/ApiVersioning/NullRequestedApiVersion.cs @@ -0,0 +1,14 @@ +namespace Volo.Abp.ApiVersioning +{ + public class NullRequestedApiVersion : IRequestedApiVersion + { + public static NullRequestedApiVersion Instance = new NullRequestedApiVersion(); + + public string Current => null; + + private NullRequestedApiVersion() + { + + } + } +} \ No newline at end of file diff --git a/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/AbpAspNetCoreMvcTestModule.cs b/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs similarity index 95% rename from test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/AbpAspNetCoreMvcTestModule.cs rename to test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs index e6cf1598b8..15b8727063 100644 --- a/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/AbpAspNetCoreMvcTestModule.cs +++ b/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs @@ -2,14 +2,13 @@ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Modularity; -using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.AspNetCore.TestBase; using Volo.Abp.Autofac; using Volo.Abp.MemoryDb; using Volo.Abp.Modularity; using Volo.Abp.TestApp; -namespace Volo.Abp.AspNetCore.App +namespace Volo.Abp.AspNetCore.Mvc { [DependsOn( typeof(AbpAspNetCoreTestBaseModule), diff --git a/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/Startup.cs b/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Startup.cs similarity index 95% rename from test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/Startup.cs rename to test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Startup.cs index 59bff4b91c..453ae00295 100644 --- a/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/App/Startup.cs +++ b/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Startup.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Volo.Abp.AspNetCore.App +namespace Volo.Abp.AspNetCore.Mvc { public class Startup { diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj new file mode 100644 index 0000000000..8253085d67 --- /dev/null +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj @@ -0,0 +1,51 @@ + + + + netcoreapp2.0 + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + Volo.Abp.AspNetCore.Mvc.Versioning.Tests + Volo.Abp.AspNetCore.Mvc.Versioning.Tests + true + true + false + false + false + true + true + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + + + + + + + + + + + + + diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs new file mode 100644 index 0000000000..bf899b8436 --- /dev/null +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs @@ -0,0 +1,48 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AspNetCore.Modularity; +using Volo.Abp.AspNetCore.Mvc.Versioning.App; +using Volo.Abp.AspNetCore.TestBase; +using Volo.Abp.Autofac; +using Volo.Abp.Http.Client; +using Volo.Abp.Modularity; + +namespace Volo.Abp.AspNetCore.Mvc.Versioning +{ + [DependsOn( + typeof(AbpAspNetCoreTestBaseModule), + typeof(AbpAspNetCoreMvcModule), + typeof(AbpAutofacModule), + typeof(AbpHttpClientModule) + )] + public class AbpAspNetCoreMvcVersioningTestModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddMvc(); + + services.Configure(options => + { + options.AppServiceControllers.Create(typeof(AbpAspNetCoreMvcVersioningTestModule).Assembly, opts => + { + + }); + }); + + services.AddAssemblyOf(); + + services.AddHttpClientProxy(); + + services.Configure(options => + { + options.RemoteServices.Default = new RemoteServiceConfiguration("/"); + }); + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + var app = context.GetApplicationBuilder(); + app.UseMvcWithDefaultRoute(); + } + } +} diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/ITodoAppService.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/ITodoAppService.cs new file mode 100644 index 0000000000..f4de87629d --- /dev/null +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/ITodoAppService.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Application.Services; + +namespace Volo.Abp.AspNetCore.Mvc.Versioning.App +{ + public interface ITodoAppService : IApplicationService + { + string Get(int id); + } +} \ No newline at end of file diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/TodoAppService.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/TodoAppService.cs new file mode 100644 index 0000000000..259ea767bd --- /dev/null +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/TodoAppService.cs @@ -0,0 +1,24 @@ +using Volo.Abp.ApiVersioning; + +namespace Volo.Abp.AspNetCore.Mvc.Versioning.App +{ + public class TodoAppService : ITodoAppService + { + private readonly IRequestedApiVersion _requestedApiVersion; + + public TodoAppService(IRequestedApiVersion requestedApiVersion) + { + _requestedApiVersion = requestedApiVersion; + } + + public string Get(int id) + { + return id + "-" + GetVersionOrNone(); + } + + private string GetVersionOrNone() + { + return _requestedApiVersion.Current ?? "NONE"; + } + } +} diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AspNetCoreMvcVersioningTestBase.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AspNetCoreMvcVersioningTestBase.cs new file mode 100644 index 0000000000..48a926cbf0 --- /dev/null +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AspNetCoreMvcVersioningTestBase.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.AspNetCore.Mvc.Versioning +{ + public abstract class AspNetCoreMvcVersioningTestBase : AbpAspNetCoreTestBase + { + } +} \ No newline at end of file diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Startup.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Startup.cs new file mode 100644 index 0000000000..4d9f831670 --- /dev/null +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Startup.cs @@ -0,0 +1,27 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Volo.Abp.AspNetCore.Mvc.Versioning +{ + public class Startup + { + public IServiceProvider ConfigureServices(IServiceCollection services) + { + services.AddApplication(options => + { + options.UseAutofac(); + }); + + //TODO: This is needed because ASP.NET Core does not use IServiceProviderFactory! + return services.BuildServiceProviderFromFactory(); + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + app.InitializeApplication(); + } + } +} diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/TodoAppService_Tests.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/TodoAppService_Tests.cs new file mode 100644 index 0000000000..66644b1a05 --- /dev/null +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/TodoAppService_Tests.cs @@ -0,0 +1,23 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.AspNetCore.Mvc.Versioning.App; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.Versioning.Test +{ + public class TodoAppService_Tests : AspNetCoreMvcVersioningTestBase + { + private readonly ITodoAppService _todoAppService; + + public TodoAppService_Tests() + { + _todoAppService = ServiceProvider.GetRequiredService(); + } + + [Fact] + public void Get() + { + _todoAppService.Get(42).ShouldBe("42-NONE"); + } + } +} diff --git a/test/Volo.Abp.AspNetCore.Tests/Volo/Abp/AspNetCore/AppTestBase.cs b/test/Volo.Abp.AspNetCore.Tests/Volo/Abp/AspNetCore/AbpAspNetCoreTestBase.cs similarity index 100% rename from test/Volo.Abp.AspNetCore.Tests/Volo/Abp/AspNetCore/AppTestBase.cs rename to test/Volo.Abp.AspNetCore.Tests/Volo/Abp/AspNetCore/AbpAspNetCoreTestBase.cs diff --git a/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpTestBase.cs b/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpTestBase.cs index f950193109..d2db4dc3ac 100644 --- a/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpTestBase.cs +++ b/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpTestBase.cs @@ -1,5 +1,4 @@ using Volo.Abp.AspNetCore; -using Volo.Abp.AspNetCore.App; namespace Volo.Abp.Http { diff --git a/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpTestModule.cs b/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpTestModule.cs index a1645af33a..abe8065b75 100644 --- a/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpTestModule.cs +++ b/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/AbpHttpTestModule.cs @@ -1,5 +1,5 @@ using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.AspNetCore.App; +using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.Http.Client; using Volo.Abp.Http.DynamicProxying; using Volo.Abp.Modularity; From 515bd63f1c160ca55329a4e6c14b2a6408599516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 5 Oct 2017 16:56:22 +0300 Subject: [PATCH 14/23] Send version for query string or path in C# client. --- .../Mvc/AbpControllerAssemblySetting.cs | 2 +- .../AspNetCoreApiDescriptionModelProvider.cs | 4 +- ...lectionDynamicHttpClientProxyExtensions.cs | 1 + .../Client/DynamicProxying/ApiVersionInfo.cs | 22 +++++++++++ .../DynamicHttpProxyInterceptor.cs | 33 +++++++++++++--- .../HttpActionParameterHelper.cs | 2 +- .../Http/Client/DynamicProxying/UrlBuilder.cs | 38 ++++++++++++------- .../Modeling/ActionApiDescriptionModel.cs | 13 ++++++- .../Modeling/ParameterApiDescriptionModel.cs | 2 +- .../Generic/AbpDictionaryExtensions.cs | 28 ++++++++++++++ ...Abp.AspNetCore.Mvc.Versioning.Tests.csproj | 3 -- .../AbpAspNetCoreMvcVersioningTestModule.cs | 28 ++++++++++++-- .../Versioning/App/Compat/ITodoAppService.cs | 9 +++++ .../Versioning/App/Compat/TodoAppService.cs | 24 ++++++++++++ .../Mvc/Versioning/App/HelloController.cs | 17 +++++++++ .../Mvc/Versioning/App/IHelloController.cs | 10 +++++ .../Test/Compat/TodoAppService_Tests.cs | 23 +++++++++++ .../Versioning/Test/HelloController_Tests.cs | 24 ++++++++++++ .../Versioning/Test/TodoAppService_Tests.cs | 2 +- 19 files changed, 254 insertions(+), 31 deletions(-) create mode 100644 src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiVersionInfo.cs create mode 100644 test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/ITodoAppService.cs create mode 100644 test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/TodoAppService.cs create mode 100644 test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/HelloController.cs create mode 100644 test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/IHelloController.cs create mode 100644 test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/Compat/TodoAppService_Tests.cs create mode 100644 test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/HelloController_Tests.cs diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs index be40f2d95f..a86d5d920f 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs @@ -43,7 +43,7 @@ namespace Volo.Abp.AspNetCore.Mvc [CanBeNull] public Func UrlActionNameNormalizer { get; set; } - public List ApiVersions { get; set; } + public List ApiVersions { get; } public Action ApiVersionConfigurer { get; set; } diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs index 594850d10c..53359745fd 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; @@ -80,7 +81,8 @@ namespace Volo.Abp.AspNetCore.Mvc var actionModel = controllerModel.AddAction(uniqueMethodName, ActionApiDescriptionModel.Create( method, apiDescription.RelativePath, - apiDescription.HttpMethod + apiDescription.HttpMethod, + setting?.ApiVersions.Select(v => v.ToString()).ToList() ?? new List() )); AddParameterDescriptionsToModel(actionModel, method, apiDescription); diff --git a/src/Volo.Abp.Http.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDynamicHttpClientProxyExtensions.cs b/src/Volo.Abp.Http.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDynamicHttpClientProxyExtensions.cs index c3ec5c76b5..d68d21ec3e 100644 --- a/src/Volo.Abp.Http.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDynamicHttpClientProxyExtensions.cs +++ b/src/Volo.Abp.Http.Client/Microsoft/Extensions/DependencyInjection/ServiceCollectionDynamicHttpClientProxyExtensions.cs @@ -15,6 +15,7 @@ namespace Microsoft.Extensions.DependencyInjection public static IServiceCollection AddHttpClientProxies(this IServiceCollection services, Assembly assembly, string remoteServiceName = RemoteServiceConfigurationDictionary.DefaultName) { + //TODO: Make a configuration option and add remoteServiceName inside it! //TODO: Add option to change type filter var serviceTypes = assembly.GetTypes().Where(t => diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiVersionInfo.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiVersionInfo.cs new file mode 100644 index 0000000000..f4239ad89f --- /dev/null +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiVersionInfo.cs @@ -0,0 +1,22 @@ +using System; + +namespace Volo.Abp.Http.Client.DynamicProxying +{ + internal class ApiVersionInfo + { + public string BindingSource { get; } + public string Version { get; } + + public ApiVersionInfo(string bindingSource, string version) + { + BindingSource = bindingSource; + Version = version; + } + + public bool ShouldSendInQueryString() + { + //TODO: Constant! TODO: Other sources! + return !BindingSource.IsIn("Path"); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs index 81b88f59f4..d9bcdcc67d 100644 --- a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs @@ -57,7 +57,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying else { var responseAsString = AsyncHelper.RunSync(() => MakeRequest(invocation)); - + //TODO: Think on that if (TypeHelper.IsPrimitiveExtendedIncludingNullable(invocation.Method.ReturnType, true)) { @@ -105,10 +105,9 @@ namespace Volo.Abp.Http.Client.DynamicProxying using (var client = _httpClientFactory.Create()) { var baseUrl = GetBaseUrl(); - var version = GetVersion(); //TODO: Add version to the request (querystring, media type, path value or custom header!) - var action = await _apiDescriptionFinder.FindActionAsync(baseUrl, typeof(TService), invocation.Method); - var url = baseUrl + UrlBuilder.GenerateUrlWithParameters(action, invocation.ArgumentsDictionary); + var apiVersion = GetApiVersionInfo(action); + var url = baseUrl + UrlBuilder.GenerateUrlWithParameters(action, invocation.ArgumentsDictionary, apiVersion); var requestMessage = new HttpRequestMessage(action.GetHttpMethod(), url) { @@ -128,6 +127,30 @@ namespace Volo.Abp.Http.Client.DynamicProxying } } + private ApiVersionInfo GetApiVersionInfo(ActionApiDescriptionModel action) + { + var apiVersion = FindBestApiVersion(action); + var versionParam = action.Parameters.FirstOrDefault(p => p.Name == "apiVersion"); + return new ApiVersionInfo(versionParam?.BindingSourceId, apiVersion); + } + + private string FindBestApiVersion(ActionApiDescriptionModel action) + { + var configuredVersion = GetConfiguredApiVersion(); + + if (action.SupportedVersions.IsNullOrEmpty()) + { + return configuredVersion ?? "1.0"; + } + + if (action.SupportedVersions.Contains(configuredVersion)) + { + return configuredVersion; + } + + return action.SupportedVersions.Last(); //TODO: Ensure to get the latest version! + } + private static void AddHeaders(IAbpMethodInvocation invocation, ActionApiDescriptionModel action, HttpRequestMessage requestMessage) { foreach (var headerParameter in action.Parameters.Where(p => p.BindingSourceId == ParameterBindingSources.Header)) @@ -150,7 +173,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying ?? throw new AbpException($"Could not find Base URL for {typeof(TService).FullName}."); } - private string GetVersion() + private string GetConfiguredApiVersion() { var clientConfig = _clientOptions.HttpClientProxies.GetOrDefault(typeof(TService)) ?? throw new AbpException($"Could not get DynamicHttpClientProxyConfig for {typeof(TService).FullName}."); diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/HttpActionParameterHelper.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/HttpActionParameterHelper.cs index 5eb5eba1de..36c3171dc2 100644 --- a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/HttpActionParameterHelper.cs +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/HttpActionParameterHelper.cs @@ -8,7 +8,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying { public static object FindParameterValue(IReadOnlyDictionary methodArguments, ParameterApiDescriptionModel apiParameter) { - var value = methodArguments[apiParameter.NameOnMethod]; + var value = methodArguments.GetOrDefault(apiParameter.NameOnMethod); if (value == null) { return null; diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/UrlBuilder.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/UrlBuilder.cs index f829c9a7a7..6a1575c24e 100644 --- a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/UrlBuilder.cs +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/UrlBuilder.cs @@ -9,17 +9,17 @@ namespace Volo.Abp.Http.Client.DynamicProxying { internal static class UrlBuilder { - public static string GenerateUrlWithParameters(ActionApiDescriptionModel action, IReadOnlyDictionary methodArguments) + public static string GenerateUrlWithParameters(ActionApiDescriptionModel action, IReadOnlyDictionary methodArguments, ApiVersionInfo apiVersion) { var urlBuilder = new StringBuilder(action.Url); - ReplacePathVariables(urlBuilder, action.Parameters, methodArguments); - AddQueryStringParameters(urlBuilder, action.Parameters, methodArguments); + ReplacePathVariables(urlBuilder, action.Parameters, methodArguments, apiVersion); + AddQueryStringParameters(urlBuilder, action.Parameters, methodArguments, apiVersion); return urlBuilder.ToString(); } - private static void ReplacePathVariables(StringBuilder urlBuilder, IList actionParameters, IReadOnlyDictionary methodArguments) + private static void ReplacePathVariables(StringBuilder urlBuilder, IList actionParameters, IReadOnlyDictionary methodArguments, ApiVersionInfo apiVersion) { var pathParameters = actionParameters .Where(p => p.BindingSourceId == ParameterBindingSources.Path) @@ -30,9 +30,15 @@ namespace Volo.Abp.Http.Client.DynamicProxying return; } - foreach (var pathParameter in pathParameters) + if (pathParameters.Any(p => p.Name == "apiVersion")) + { + urlBuilder = urlBuilder.Replace("{apiVersion}", apiVersion.Version); + } + + foreach (var pathParameter in pathParameters.Where(p => p.Name != "apiVersion")) //TODO: Constant! { var value = HttpActionParameterHelper.FindParameterValue(methodArguments, pathParameter); + if (value == null) { if (pathParameter.IsOptional) @@ -55,18 +61,14 @@ namespace Volo.Abp.Http.Client.DynamicProxying } } - private static void AddQueryStringParameters(StringBuilder urlBuilder, IList actionParameters, IReadOnlyDictionary methodArguments) + private static void AddQueryStringParameters(StringBuilder urlBuilder, IList actionParameters, IReadOnlyDictionary methodArguments, ApiVersionInfo apiVersion) { var queryStringParameters = actionParameters .Where(p => p.BindingSourceId.IsIn(ParameterBindingSources.ModelBinding, ParameterBindingSources.Query)) .ToArray(); - if (!queryStringParameters.Any()) - { - return; - } - var isFirstParam = true; + foreach (var queryStringParameter in queryStringParameters) { var value = HttpActionParameterHelper.FindParameterValue(methodArguments, queryStringParameter); @@ -75,11 +77,21 @@ namespace Volo.Abp.Http.Client.DynamicProxying continue; } - urlBuilder.Append(isFirstParam ? "?" : "&"); - urlBuilder.Append(queryStringParameter.Name + "=" + System.Net.WebUtility.UrlEncode(value.ToString())); + AddQueryStringParameter(urlBuilder, isFirstParam, queryStringParameter.Name, value); isFirstParam = false; } + + if (apiVersion.ShouldSendInQueryString()) + { + AddQueryStringParameter(urlBuilder, isFirstParam, "apiVersion", apiVersion.Version); //TODO: Constant! + } + } + + private static void AddQueryStringParameter(StringBuilder urlBuilder, bool isFirstParam, string name, object value) + { + urlBuilder.Append(isFirstParam ? "?" : "&"); + urlBuilder.Append(name + "=" + System.Net.WebUtility.UrlEncode(value.ToString())); } } } diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs index ffed6f2184..86d903f97f 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Reflection; +using JetBrains.Annotations; namespace Volo.Abp.Http.Modeling { @@ -15,6 +16,8 @@ namespace Volo.Abp.Http.Modeling public string Url { get; set; } + public IList SupportedVersions { get; set; } + public IList ParametersOnMethod { get; set; } public IList Parameters { get; set; } @@ -26,8 +29,13 @@ namespace Volo.Abp.Http.Modeling } - public static ActionApiDescriptionModel Create(MethodInfo method, string url, string httpMethod) + public static ActionApiDescriptionModel Create([NotNull] MethodInfo method, [NotNull] string url, [NotNull] string httpMethod, [NotNull] IList supportedVersions) { + Check.NotNull(method, nameof(method)); + Check.NotNull(url, nameof(url)); + Check.NotNull(httpMethod, nameof(httpMethod)); + Check.NotNull(supportedVersions, nameof(supportedVersions)); + return new ActionApiDescriptionModel { Name = method.Name, @@ -38,7 +46,8 @@ namespace Volo.Abp.Http.Modeling ParametersOnMethod = method .GetParameters() .Select(MethodParameterApiDescriptionModel.Create) - .ToList() + .ToList(), + SupportedVersions = supportedVersions }; } diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ParameterApiDescriptionModel.cs b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ParameterApiDescriptionModel.cs index 609fdd9000..d1de33258f 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ParameterApiDescriptionModel.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ParameterApiDescriptionModel.cs @@ -30,7 +30,7 @@ namespace Volo.Abp.Http.Modeling { Name = name, NameOnMethod = nameOnMethod, - TypeAsString = type.GetFullNameWithAssemblyName(), + TypeAsString = type?.GetFullNameWithAssemblyName(), IsOptional = isOptional, DefaultValue = defaultValue, ConstraintTypes = constraintTypes, diff --git a/src/Volo.Abp/System/Collections/Generic/AbpDictionaryExtensions.cs b/src/Volo.Abp/System/Collections/Generic/AbpDictionaryExtensions.cs index 47ceca79f2..bac4b58217 100644 --- a/src/Volo.Abp/System/Collections/Generic/AbpDictionaryExtensions.cs +++ b/src/Volo.Abp/System/Collections/Generic/AbpDictionaryExtensions.cs @@ -26,6 +26,20 @@ namespace System.Collections.Generic return false; } + /// + /// Gets a value from the dictionary with given key. Returns default value if can not find. + /// + /// Dictionary to check and get + /// Key to find the value + /// Type of the key + /// Type of the value + /// Value if found, default if can not found. + public static TValue GetOrDefault(this Dictionary dictionary, TKey key) + { + TValue obj; + return dictionary.TryGetValue(key, out obj) ? obj : default(TValue); + } + /// /// Gets a value from the dictionary with given key. Returns default value if can not find. /// @@ -40,6 +54,20 @@ namespace System.Collections.Generic return dictionary.TryGetValue(key, out obj) ? obj : default(TValue); } + /// + /// Gets a value from the dictionary with given key. Returns default value if can not find. + /// + /// Dictionary to check and get + /// Key to find the value + /// Type of the key + /// Type of the value + /// Value if found, default if can not found. + public static TValue GetOrDefault(this IReadOnlyDictionary dictionary, TKey key) + { + TValue obj; + return dictionary.TryGetValue(key, out obj) ? obj : default(TValue); + } + /// /// Gets a value from the dictionary with given key. Returns default value if can not find. /// diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj index 8253085d67..3db24955e1 100644 --- a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj @@ -35,9 +35,6 @@ PreserveNewest - - - diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs index bf899b8436..52860d2ad2 100644 --- a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs @@ -1,4 +1,6 @@ using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Versioning; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Modularity; using Volo.Abp.AspNetCore.Mvc.Versioning.App; @@ -23,15 +25,35 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning services.Configure(options => { + //2.0 Version options.AppServiceControllers.Create(typeof(AbpAspNetCoreMvcVersioningTestModule).Assembly, opts => { - + opts.TypePredicate = t => t.Namespace == typeof(Volo.Abp.AspNetCore.Mvc.Versioning.App.TodoAppService).Namespace; + opts.ApiVersions.Add(new ApiVersion(2, 0)); + }); + + //1.0 Compatability version + options.AppServiceControllers.Create(typeof(AbpAspNetCoreMvcVersioningTestModule).Assembly, opts => + { + opts.TypePredicate = t => t.Namespace == typeof(Volo.Abp.AspNetCore.Mvc.Versioning.App.Compat.TodoAppService).Namespace; + opts.ApiVersions.Add(new ApiVersion(1, 0)); + opts.RootPath = "app/compat"; }); }); - services.AddAssemblyOf(); + services.AddApiVersioning(options => + { + options.ReportApiVersions = true; + options.AssumeDefaultVersionWhenUnspecified = true; + + options.ApiVersionReader = new QueryStringApiVersionReader("apiVersion"); - services.AddHttpClientProxy(); + options.ConfigureAbp(services); + }); + + services.AddAssemblyOf(); + + services.AddHttpClientProxies(typeof(AbpAspNetCoreMvcVersioningTestModule).Assembly); services.Configure(options => { diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/ITodoAppService.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/ITodoAppService.cs new file mode 100644 index 0000000000..0a8ed290ce --- /dev/null +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/ITodoAppService.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Application.Services; + +namespace Volo.Abp.AspNetCore.Mvc.Versioning.App.Compat +{ + public interface ITodoAppService : IApplicationService + { + string Get(int id); + } +} \ No newline at end of file diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/TodoAppService.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/TodoAppService.cs new file mode 100644 index 0000000000..83835fa9b0 --- /dev/null +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/TodoAppService.cs @@ -0,0 +1,24 @@ +using Volo.Abp.ApiVersioning; + +namespace Volo.Abp.AspNetCore.Mvc.Versioning.App.Compat +{ + public class TodoAppService : ITodoAppService + { + private readonly IRequestedApiVersion _requestedApiVersion; + + public TodoAppService(IRequestedApiVersion requestedApiVersion) + { + _requestedApiVersion = requestedApiVersion; + } + + public string Get(int id) + { + return "Compat-" + id + "-" + GetVersionOrNone(); + } + + private string GetVersionOrNone() + { + return _requestedApiVersion.Current ?? "NONE"; + } + } +} diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/HelloController.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/HelloController.cs new file mode 100644 index 0000000000..fb39ae53ed --- /dev/null +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/HelloController.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace Volo.Abp.AspNetCore.Mvc.Versioning.App +{ + [ApiVersion("1.0")] + [ApiVersion("2.0")] + [Route("api/v{apiVersion:apiVersion}/[controller]")] + public class HelloController : AbpController, IHelloController + { + [HttpPost] + public Task PostAsync() + { + return Task.FromResult($"42-{HttpContext.GetRequestedApiVersion().ToString()}"); + } + } +} \ No newline at end of file diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/IHelloController.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/IHelloController.cs new file mode 100644 index 0000000000..5f43027c8e --- /dev/null +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/IHelloController.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Volo.Abp.Application.Services; + +namespace Volo.Abp.AspNetCore.Mvc.Versioning.App +{ + public interface IHelloController : IRemoteService + { + Task PostAsync(); + } +} diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/Compat/TodoAppService_Tests.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/Compat/TodoAppService_Tests.cs new file mode 100644 index 0000000000..0ce095c470 --- /dev/null +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/Compat/TodoAppService_Tests.cs @@ -0,0 +1,23 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.AspNetCore.Mvc.Versioning.App.Compat; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.Versioning.Test.Compat +{ + public class TodoAppService_Tests : AspNetCoreMvcVersioningTestBase + { + private readonly ITodoAppService _todoAppService; + + public TodoAppService_Tests() + { + _todoAppService = ServiceProvider.GetRequiredService(); + } + + [Fact] + public void Get() + { + _todoAppService.Get(42).ShouldBe("Compat-42-1.0"); + } + } +} diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/HelloController_Tests.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/HelloController_Tests.cs new file mode 100644 index 0000000000..519bfffd11 --- /dev/null +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/HelloController_Tests.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.AspNetCore.Mvc.Versioning.App; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.Versioning.Test +{ + public class HelloController_Tests: AspNetCoreMvcVersioningTestBase + { + private readonly IHelloController _todoAppService; + + public HelloController_Tests() + { + _todoAppService = ServiceProvider.GetRequiredService(); + } + + [Fact] + public async Task PostAsync() + { + (await _todoAppService.PostAsync()).ShouldBe("42-2.0"); + } + } +} diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/TodoAppService_Tests.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/TodoAppService_Tests.cs index 66644b1a05..90ea5970b0 100644 --- a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/TodoAppService_Tests.cs +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/TodoAppService_Tests.cs @@ -17,7 +17,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning.Test [Fact] public void Get() { - _todoAppService.Get(42).ShouldBe("42-NONE"); + _todoAppService.Get(42).ShouldBe("42-2.0"); } } } From a3bd9c8c72d8bedfbec612916686a73ea0c46539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 5 Oct 2017 17:29:48 +0300 Subject: [PATCH 15/23] Improvements on api versioning. --- .../Client/DynamicProxying/DynamicHttpProxyInterceptor.cs | 6 +++++- .../Volo/Abp/Http/Client/DynamicProxying/UrlBuilder.cs | 2 +- .../Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs index d9bcdcc67d..6cd82d37db 100644 --- a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs @@ -130,7 +130,11 @@ namespace Volo.Abp.Http.Client.DynamicProxying private ApiVersionInfo GetApiVersionInfo(ActionApiDescriptionModel action) { var apiVersion = FindBestApiVersion(action); - var versionParam = action.Parameters.FirstOrDefault(p => p.Name == "apiVersion"); + + //TODO: Make names configurable! + var versionParam = action.Parameters.FirstOrDefault(p => p.Name == "apiVersion") ?? + action.Parameters.FirstOrDefault(p => p.Name == "api-version" && p.BindingSourceId == ParameterBindingSources.Query); + return new ApiVersionInfo(versionParam?.BindingSourceId, apiVersion); } diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/UrlBuilder.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/UrlBuilder.cs index 6a1575c24e..dcb9868b7f 100644 --- a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/UrlBuilder.cs +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/UrlBuilder.cs @@ -84,7 +84,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying if (apiVersion.ShouldSendInQueryString()) { - AddQueryStringParameter(urlBuilder, isFirstParam, "apiVersion", apiVersion.Version); //TODO: Constant! + AddQueryStringParameter(urlBuilder, isFirstParam, "api-version", apiVersion.Version); //TODO: Constant! } } diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs index 52860d2ad2..e5533665c3 100644 --- a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs @@ -46,7 +46,8 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning options.ReportApiVersions = true; options.AssumeDefaultVersionWhenUnspecified = true; - options.ApiVersionReader = new QueryStringApiVersionReader("apiVersion"); + //options.ApiVersionReader = new UrlSegmentApiVersionReader(); + //options.ApiVersionReader = new MediaTypeApiVersionReader(); options.ConfigureAbp(services); }); From f022b04f7a7ad5a256fdf319015f198daf476184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 5 Oct 2017 18:32:16 +0300 Subject: [PATCH 16/23] Implemented media type version selector for c# client. --- .../Client/DynamicProxying/ApiVersionInfo.cs | 2 +- .../DynamicHttpProxyInterceptor.cs | 18 ++++++++++++++---- .../DynamicProxying/RequestPayloadBuilder.cs | 3 +-- .../AbpAspNetCoreMvcVersioningTestModule.cs | 5 ----- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiVersionInfo.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiVersionInfo.cs index f4239ad89f..88035cf94b 100644 --- a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiVersionInfo.cs +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/ApiVersionInfo.cs @@ -2,7 +2,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying { - internal class ApiVersionInfo + public class ApiVersionInfo //TODO: Rename to not conflict with api versioning apis { public string BindingSource { get; } public string Version { get; } diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs index 6cd82d37db..29e2bff51b 100644 --- a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; +using System.Net.Http.Headers; using System.Reflection; using System.Threading.Tasks; using Microsoft.Extensions.Options; @@ -111,10 +112,10 @@ namespace Volo.Abp.Http.Client.DynamicProxying var requestMessage = new HttpRequestMessage(action.GetHttpMethod(), url) { - Content = RequestPayloadBuilder.BuildContent(action, invocation.ArgumentsDictionary, _jsonSerializer) + Content = RequestPayloadBuilder.BuildContent(action, invocation.ArgumentsDictionary, _jsonSerializer, apiVersion) }; - AddHeaders(invocation, action, requestMessage); + AddHeaders(invocation, action, requestMessage, apiVersion); var response = await client.SendAsync(requestMessage); @@ -155,9 +156,18 @@ namespace Volo.Abp.Http.Client.DynamicProxying return action.SupportedVersions.Last(); //TODO: Ensure to get the latest version! } - private static void AddHeaders(IAbpMethodInvocation invocation, ActionApiDescriptionModel action, HttpRequestMessage requestMessage) + private static void AddHeaders(IAbpMethodInvocation invocation, ActionApiDescriptionModel action, HttpRequestMessage requestMessage, ApiVersionInfo apiVersion) { - foreach (var headerParameter in action.Parameters.Where(p => p.BindingSourceId == ParameterBindingSources.Header)) + if (!apiVersion.Version.IsNullOrEmpty()) + { + //TODO: What about other media types? + requestMessage.Headers.Add("accept", $"text/plain; v={apiVersion.Version}"); + requestMessage.Headers.Add("accept", $"application/json; v={apiVersion.Version}"); + } + + var headers = action.Parameters.Where(p => p.BindingSourceId == ParameterBindingSources.Header).ToArray(); + + foreach (var headerParameter in headers) { var value = HttpActionParameterHelper.FindParameterValue(invocation.ArgumentsDictionary, headerParameter); if (value != null) diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/RequestPayloadBuilder.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/RequestPayloadBuilder.cs index f58bda5b9c..8f5fbd0c39 100644 --- a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/RequestPayloadBuilder.cs +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/RequestPayloadBuilder.cs @@ -12,10 +12,9 @@ namespace Volo.Abp.Http.Client.DynamicProxying public static class RequestPayloadBuilder { [CanBeNull] - public static HttpContent BuildContent(ActionApiDescriptionModel action,IReadOnlyDictionary methodArguments, IJsonSerializer jsonSerializer) + public static HttpContent BuildContent(ActionApiDescriptionModel action,IReadOnlyDictionary methodArguments, IJsonSerializer jsonSerializer, ApiVersionInfo apiVersion) { var body = GenerateBody(action, methodArguments, jsonSerializer); - if (body != null) { return new StringContent(body, Encoding.UTF8, "application/json"); //TODO: application/json to a constant diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs index e5533665c3..976a5331b1 100644 --- a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs @@ -1,9 +1,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Versioning; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Modularity; -using Volo.Abp.AspNetCore.Mvc.Versioning.App; using Volo.Abp.AspNetCore.TestBase; using Volo.Abp.Autofac; using Volo.Abp.Http.Client; @@ -46,9 +44,6 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning options.ReportApiVersions = true; options.AssumeDefaultVersionWhenUnspecified = true; - //options.ApiVersionReader = new UrlSegmentApiVersionReader(); - //options.ApiVersionReader = new MediaTypeApiVersionReader(); - options.ConfigureAbp(services); }); From f1780f2841dc3ad41e6008616084a0a3d6c0bf2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 5 Oct 2017 18:38:42 +0300 Subject: [PATCH 17/23] Remove unnecessary root path for api versioning tests. --- .../Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs index 976a5331b1..4c82e1cf62 100644 --- a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs @@ -35,7 +35,6 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning { opts.TypePredicate = t => t.Namespace == typeof(Volo.Abp.AspNetCore.Mvc.Versioning.App.Compat.TodoAppService).Namespace; opts.ApiVersions.Add(new ApiVersion(1, 0)); - opts.RootPath = "app/compat"; }); }); From 601228ab41f8456a43537a92f2424f4a9154a15a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 5 Oct 2017 18:44:34 +0300 Subject: [PATCH 18/23] Support --- .../Client/DynamicProxying/DynamicHttpProxyInterceptor.cs | 1 + .../Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs index 29e2bff51b..dee7b66238 100644 --- a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs @@ -163,6 +163,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying //TODO: What about other media types? requestMessage.Headers.Add("accept", $"text/plain; v={apiVersion.Version}"); requestMessage.Headers.Add("accept", $"application/json; v={apiVersion.Version}"); + requestMessage.Headers.Add("api-version", apiVersion.Version); } var headers = action.Parameters.Where(p => p.BindingSourceId == ParameterBindingSources.Header).ToArray(); diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs index 4c82e1cf62..68a50f8416 100644 --- a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Versioning; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Modularity; using Volo.Abp.AspNetCore.TestBase; @@ -43,6 +44,9 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning options.ReportApiVersions = true; options.AssumeDefaultVersionWhenUnspecified = true; + //options.ApiVersionReader = new HeaderApiVersionReader("api-version"); //Supports header too + //options.ApiVersionReader = new MediaTypeApiVersionReader(); //Supports accept header too + options.ConfigureAbp(services); }); From 29a7cfe9b2330212f412ed0b0e8805cda36879e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 5 Oct 2017 19:09:39 +0300 Subject: [PATCH 19/23] Refactored. --- .../AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs | 2 +- .../AbpApiVersioningOptionsExtensions.cs | 5 +-- .../AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs | 9 ++--- .../AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs | 9 ++--- .../AspNetCore/Mvc/AbpMvcOptionsExtensions.cs | 3 +- .../Mvc/AppServiceControllerOptions.cs | 35 ------------------- .../AspNetCoreApiDescriptionModelProvider.cs | 9 ++--- ...pConventionalControllerFeatureProvider.cs} | 10 +++--- .../AbpServiceConvention.cs} | 35 +++++++++---------- .../AbpServiceConventionWrapper.cs} | 10 +++--- .../ConventionalControllerOptions.cs | 35 +++++++++++++++++++ .../ConventionalControllerSetting.cs} | 6 ++-- .../ConventionalControllerSettingList.cs} | 6 ++-- .../Mvc/Conventions/IAbpServiceConvention.cs | 8 +++++ .../UrlActionNameNormalizerContext.cs | 2 +- .../UrlControllerNameNormalizerContext.cs | 2 +- .../Mvc/IAbpAppServiceConvention.cs | 8 ----- .../AbpIdentityHttpApiHostModule.cs | 5 ++- .../SwaggerDefaultValues.cs | 10 +++--- .../Abp/Identity/AbpIdentityHttpApiModule.cs | 4 +-- .../Mvc/AbpAspNetCoreMvcTestModule.cs | 2 +- .../AbpAspNetCoreMvcVersioningTestModule.cs | 4 +-- 22 files changed, 111 insertions(+), 108 deletions(-) delete mode 100644 src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AppServiceControllerOptions.cs rename src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/{AbpAppServiceControllerFeatureProvider.cs => Conventions/AbpConventionalControllerFeatureProvider.cs} (68%) rename src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/{AbpAppServiceConvention.cs => Conventions/AbpServiceConvention.cs} (88%) rename src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/{AbpAppServiceConventionWrapper.cs => Conventions/AbpServiceConventionWrapper.cs} (50%) create mode 100644 src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/ConventionalControllerOptions.cs rename src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/{AbpControllerAssemblySetting.cs => Conventions/ConventionalControllerSetting.cs} (92%) rename src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/{ControllerAssemblySettingList.cs => Conventions/ConventionalControllerSettingList.cs} (55%) create mode 100644 src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/IAbpServiceConvention.cs rename src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/{ => Conventions}/UrlActionNameNormalizerContext.cs (93%) rename src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/{ => Conventions}/UrlControllerNameNormalizerContext.cs (87%) delete mode 100644 src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/IAbpAppServiceConvention.cs diff --git a/src/AbpDesk/AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs b/src/AbpDesk/AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs index 24addb659f..50c18af252 100644 --- a/src/AbpDesk/AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs +++ b/src/AbpDesk/AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs @@ -65,7 +65,7 @@ namespace AbpDesk.Web.Mvc services.Configure(options => { - options.AppServiceControllers.Create(typeof(AbpDeskApplicationModule).Assembly); + options.ConventionalControllers.Create(typeof(AbpDeskApplicationModule).Assembly); }); } diff --git a/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs b/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs index 7137d7fe3d..c792b563d1 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc.Versioning; using Microsoft.AspNetCore.Mvc.Versioning.Conventions; using Volo.Abp.ApiVersioning; using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.Conventions; using Volo.Abp.AspNetCore.Mvc.Versioning; namespace Microsoft.Extensions.DependencyInjection @@ -22,7 +23,7 @@ namespace Microsoft.Extensions.DependencyInjection //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 - foreach (var setting in op.AppServiceControllers.ControllerAssemblySettings) + foreach (var setting in op.ConventionalControllers.ConventionalControllerSettings) { if (setting.ApiVersionConfigurer == null) { @@ -36,7 +37,7 @@ namespace Microsoft.Extensions.DependencyInjection }); } - private static void ConfigureApiVersionsByConvention(ApiVersioningOptions options, AbpControllerAssemblySetting setting) + private static void ConfigureApiVersionsByConvention(ApiVersioningOptions options, ConventionalControllerSetting setting) { foreach (var controllerType in setting.ControllerTypes) { diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs index 088936b69d..b06cefdcd1 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs @@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.ViewComponents; using Microsoft.Extensions.DependencyInjection.Extensions; +using Volo.Abp.AspNetCore.Mvc.Conventions; using Volo.Abp.Http; using Volo.Abp.Http.Modeling; @@ -53,7 +54,7 @@ namespace Volo.Abp.AspNetCore.Mvc services.Configure(options => { - options.AppServiceControllers.Create(typeof(AbpAspNetCoreMvcModule).Assembly, o => + options.ConventionalControllers.Create(typeof(AbpAspNetCoreMvcModule).Assembly, o => { o.RootPath = "abp"; }); @@ -77,7 +78,7 @@ namespace Volo.Abp.AspNetCore.Mvc var partManager = services.GetSingletonInstance(); var application = services.GetSingletonInstance(); - partManager.FeatureProviders.Add(new AbpAppServiceControllerFeatureProvider(application)); + partManager.FeatureProviders.Add(new AbpConventionalControllerFeatureProvider(application)); services.Configure(mvcOptions => { @@ -114,8 +115,8 @@ namespace Volo.Abp.AspNetCore.Mvc .ServiceProvider .GetRequiredService>() .Value - .AppServiceControllers - .ControllerAssemblySettings + .ConventionalControllers + .ConventionalControllerSettings .Select(s => s.Assembly) .Distinct(); diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs index 7d2bb24341..6dec2fde14 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs @@ -1,13 +1,14 @@ -namespace Volo.Abp.AspNetCore.Mvc +using Volo.Abp.AspNetCore.Mvc.Conventions; + +namespace Volo.Abp.AspNetCore.Mvc { public class AbpAspNetCoreMvcOptions { - //TODO: Rename to ConventionalControllers - public AppServiceControllerOptions AppServiceControllers { get; } + public ConventionalControllerOptions ConventionalControllers { get; } public AbpAspNetCoreMvcOptions() { - AppServiceControllers = new AppServiceControllerOptions(); + ConventionalControllers = new ConventionalControllerOptions(); } } } \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs index e0cb1c2232..b18d155573 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AspNetCore.Mvc.Conventions; using Volo.Abp.AspNetCore.Mvc.ExceptionHandling; using Volo.Abp.AspNetCore.Mvc.Uow; using Volo.Abp.AspNetCore.Mvc.Validation; @@ -17,7 +18,7 @@ namespace Volo.Abp.AspNetCore.Mvc private static void AddConventions(MvcOptions options, IServiceCollection services) { - options.Conventions.Add(new AbpAppServiceConventionWrapper(services)); + options.Conventions.Add(new AbpServiceConventionWrapper(services)); } private static void AddFilters(MvcOptions options) diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AppServiceControllerOptions.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AppServiceControllerOptions.cs deleted file mode 100644 index 4c677fb8e0..0000000000 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AppServiceControllerOptions.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using JetBrains.Annotations; -using Microsoft.AspNetCore.Http; -using Volo.Abp.Http.Modeling; - -namespace Volo.Abp.AspNetCore.Mvc -{ - public class AppServiceControllerOptions - { - public ControllerAssemblySettingList ControllerAssemblySettings { get; } - - public List FormBodyBindingIgnoredTypes { get; } - - public AppServiceControllerOptions() - { - ControllerAssemblySettings = new ControllerAssemblySettingList(); - - FormBodyBindingIgnoredTypes = new List - { - typeof(IFormFile) - }; - } - - public AppServiceControllerOptions Create(Assembly assembly, [CanBeNull] Action optionsAction = null) - { - var setting = new AbpControllerAssemblySetting(assembly, ModuleApiDescriptionModel.DefaultRootPath); - optionsAction?.Invoke(setting); - setting.Initialize(); - ControllerAssemblySettings.Add(setting); - return this; - } - } -} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs index 53359745fd..4ddfa24f45 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs @@ -12,6 +12,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Volo.Abp.Application.Services; +using Volo.Abp.AspNetCore.Mvc.Conventions; using Volo.Abp.AspNetCore.Mvc.Utils; using Volo.Abp.DependencyInjection; using Volo.Abp.Http.Modeling; @@ -88,7 +89,7 @@ namespace Volo.Abp.AspNetCore.Mvc AddParameterDescriptionsToModel(actionModel, method, apiDescription); } - private static string CalculateControllerName(Type controllerType, AbpControllerAssemblySetting setting) + private static string CalculateControllerName(Type controllerType, ConventionalControllerSetting setting) { var controllerName = controllerType.Name.RemovePostFix("Controller").RemovePostFix(ApplicationService.CommonPostfixes); @@ -169,7 +170,7 @@ namespace Volo.Abp.AspNetCore.Mvc return modelNameProvider.Name; } - private static string GetRootPath([NotNull] Type controllerType, [CanBeNull] AbpControllerAssemblySetting setting) + private static string GetRootPath([NotNull] Type controllerType, [CanBeNull] ConventionalControllerSetting setting) { if (setting != null) { @@ -186,9 +187,9 @@ namespace Volo.Abp.AspNetCore.Mvc } [CanBeNull] - private AbpControllerAssemblySetting FindSetting(Type controllerType) + private ConventionalControllerSetting FindSetting(Type controllerType) { - foreach (var controllerSetting in _options.AppServiceControllers.ControllerAssemblySettings) + foreach (var controllerSetting in _options.ConventionalControllers.ConventionalControllerSettings) { if (controllerSetting.ControllerTypes.Contains(controllerType)) { diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceControllerFeatureProvider.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpConventionalControllerFeatureProvider.cs similarity index 68% rename from src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceControllerFeatureProvider.cs rename to src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpConventionalControllerFeatureProvider.cs index a9165f1409..e04574a03b 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceControllerFeatureProvider.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpConventionalControllerFeatureProvider.cs @@ -3,13 +3,13 @@ using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -namespace Volo.Abp.AspNetCore.Mvc +namespace Volo.Abp.AspNetCore.Mvc.Conventions { - public class AbpAppServiceControllerFeatureProvider : ControllerFeatureProvider + public class AbpConventionalControllerFeatureProvider : ControllerFeatureProvider { private readonly IAbpApplication _application; - public AbpAppServiceControllerFeatureProvider(IAbpApplication application) + public AbpConventionalControllerFeatureProvider(IAbpApplication application) { _application = application; } @@ -19,8 +19,8 @@ namespace Volo.Abp.AspNetCore.Mvc //TODO: Move this to a lazy loaded field for efficiency. var configuration = _application.ServiceProvider .GetRequiredService>().Value - .AppServiceControllers - .ControllerAssemblySettings + .ConventionalControllers + .ConventionalControllerSettings .GetSettingOrNull(typeInfo.AsType()); return configuration != null; diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs similarity index 88% rename from src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs rename to src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs index dd261936e2..946be023cc 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConvention.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs @@ -14,13 +14,13 @@ using Volo.Abp.Http; using Volo.Abp.Http.Modeling; using Volo.Abp.Reflection; -namespace Volo.Abp.AspNetCore.Mvc +namespace Volo.Abp.AspNetCore.Mvc.Conventions { - public class AbpAppServiceConvention : IAbpAppServiceConvention, ITransientDependency + public class AbpServiceConvention : IAbpServiceConvention, ITransientDependency { private readonly AbpAspNetCoreMvcOptions _options; - public AbpAppServiceConvention(IOptions options) + public AbpServiceConvention(IOptions options) { _options = options.Value; } @@ -38,8 +38,7 @@ namespace Volo.Abp.AspNetCore.Mvc var configuration = GetControllerSettingOrNull(controllerType); //TODO: We can remove different behaviour for ImplementsRemoteServiceInterface. If there is a configuration, then it should be applied! - //TODO: If so, we can rename AppServiceControllers to ConventionalControllers! - //TODO: But also consider AbpControllerAssemblySetting.IsRemoteService method too..! + //TODO: But also consider ConventionalControllerSetting.IsRemoteService method too..! if (ImplementsRemoteServiceInterface(controllerType)) { @@ -58,7 +57,7 @@ namespace Volo.Abp.AspNetCore.Mvc } } - protected virtual void ConfigureRemoteService(ControllerModel controller, [CanBeNull] AbpControllerAssemblySetting configuration) + protected virtual void ConfigureRemoteService(ControllerModel controller, [CanBeNull] ConventionalControllerSetting configuration) { ConfigureApiExplorer(controller); ConfigureSelector(controller, configuration); @@ -95,7 +94,7 @@ namespace Volo.Abp.AspNetCore.Mvc protected virtual bool CanUseFormBodyBinding(ActionModel action, ParameterModel parameter) { - if (_options.AppServiceControllers.FormBodyBindingIgnoredTypes.Any(t => t.IsAssignableFrom(parameter.ParameterInfo.ParameterType))) + if (_options.ConventionalControllers.FormBodyBindingIgnoredTypes.Any(t => t.IsAssignableFrom(parameter.ParameterInfo.ParameterType))) { return false; } @@ -168,7 +167,7 @@ namespace Volo.Abp.AspNetCore.Mvc } } - protected virtual void ConfigureSelector(ControllerModel controller, [CanBeNull] AbpControllerAssemblySetting configuration) + protected virtual void ConfigureSelector(ControllerModel controller, [CanBeNull] ConventionalControllerSetting configuration) { RemoveEmptySelectors(controller.Selectors); @@ -185,7 +184,7 @@ namespace Volo.Abp.AspNetCore.Mvc } } - protected virtual void ConfigureSelector(string rootPath, string controllerName, ActionModel action, [CanBeNull] AbpControllerAssemblySetting configuration) + protected virtual void ConfigureSelector(string rootPath, string controllerName, ActionModel action, [CanBeNull] ConventionalControllerSetting configuration) { RemoveEmptySelectors(action.Selectors); @@ -199,7 +198,7 @@ namespace Volo.Abp.AspNetCore.Mvc } } - protected virtual void AddAbpServiceSelector(string rootPath, string controllerName, ActionModel action, [CanBeNull] AbpControllerAssemblySetting configuration) + protected virtual void AddAbpServiceSelector(string rootPath, string controllerName, ActionModel action, [CanBeNull] ConventionalControllerSetting configuration) { var httpMethod = SelectHttpMethod(action, configuration); @@ -212,12 +211,12 @@ namespace Volo.Abp.AspNetCore.Mvc action.Selectors.Add(abpServiceSelectorModel); } - protected virtual string SelectHttpMethod(ActionModel action, AbpControllerAssemblySetting configuration) + protected virtual string SelectHttpMethod(ActionModel action, ConventionalControllerSetting configuration) { return HttpMethodHelper.GetConventionalVerbForMethodName(action.ActionName); } - protected virtual void NormalizeSelectorRoutes(string rootPath, string controllerName, ActionModel action, [CanBeNull] AbpControllerAssemblySetting configuration) + protected virtual void NormalizeSelectorRoutes(string rootPath, string controllerName, ActionModel action, [CanBeNull] ConventionalControllerSetting configuration) { foreach (var selector in action.Selectors) { @@ -236,12 +235,12 @@ namespace Volo.Abp.AspNetCore.Mvc } [CanBeNull] - protected virtual AbpControllerAssemblySetting GetControllerSettingOrNull(Type controllerType) + protected virtual ConventionalControllerSetting GetControllerSettingOrNull(Type controllerType) { - return _options.AppServiceControllers.ControllerAssemblySettings.GetSettingOrNull(controllerType); + return _options.ConventionalControllers.ConventionalControllerSettings.GetSettingOrNull(controllerType); } - protected virtual AttributeRouteModel CreateAbpServiceAttributeRouteModel(string rootPath, string controllerName, ActionModel action, string httpMethod, [CanBeNull] AbpControllerAssemblySetting configuration) + protected virtual AttributeRouteModel CreateAbpServiceAttributeRouteModel(string rootPath, string controllerName, ActionModel action, string httpMethod, [CanBeNull] ConventionalControllerSetting configuration) { return new AttributeRouteModel( new RouteAttribute( @@ -250,7 +249,7 @@ namespace Volo.Abp.AspNetCore.Mvc ); } - protected virtual string CalculateRouteTemplate(string rootPath, string controllerName, ActionModel action, string httpMethod, [CanBeNull] AbpControllerAssemblySetting configuration) + protected virtual string CalculateRouteTemplate(string rootPath, string controllerName, ActionModel action, string httpMethod, [CanBeNull] ConventionalControllerSetting configuration) { var controllerNameInUrl = NormalizeUrlControllerName(rootPath, controllerName, action, httpMethod, configuration); @@ -279,7 +278,7 @@ namespace Volo.Abp.AspNetCore.Mvc return url; } - protected virtual string NormalizeUrlActionName(string rootPath, string controllerName, ActionModel action, string httpMethod, [CanBeNull] AbpControllerAssemblySetting configuration) + protected virtual string NormalizeUrlActionName(string rootPath, string controllerName, ActionModel action, string httpMethod, [CanBeNull] ConventionalControllerSetting configuration) { var actionNameInUrl = HttpMethodHelper .RemoveHttpMethodPrefix(action.ActionName, httpMethod) @@ -301,7 +300,7 @@ namespace Volo.Abp.AspNetCore.Mvc ); } - protected virtual string NormalizeUrlControllerName(string rootPath, string controllerName, ActionModel action, string httpMethod, [CanBeNull] AbpControllerAssemblySetting configuration) + protected virtual string NormalizeUrlControllerName(string rootPath, string controllerName, ActionModel action, string httpMethod, [CanBeNull] ConventionalControllerSetting configuration) { if(configuration?.UrlControllerNameNormalizer == null) { diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConventionWrapper.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConventionWrapper.cs similarity index 50% rename from src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConventionWrapper.cs rename to src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConventionWrapper.cs index c4fff972b6..10a45a659a 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAppServiceConventionWrapper.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConventionWrapper.cs @@ -3,16 +3,16 @@ using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.DependencyInjection; -namespace Volo.Abp.AspNetCore.Mvc +namespace Volo.Abp.AspNetCore.Mvc.Conventions { [DisableConventionalRegistration] - public class AbpAppServiceConventionWrapper : IApplicationModelConvention + public class AbpServiceConventionWrapper : IApplicationModelConvention { - private readonly Lazy _convention; + private readonly Lazy _convention; - public AbpAppServiceConventionWrapper(IServiceCollection services) + public AbpServiceConventionWrapper(IServiceCollection services) { - _convention = services.GetRequiredServiceLazy(); + _convention = services.GetRequiredServiceLazy(); } public void Apply(ApplicationModel application) diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/ConventionalControllerOptions.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/ConventionalControllerOptions.cs new file mode 100644 index 0000000000..a574716ef1 --- /dev/null +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/ConventionalControllerOptions.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Http; +using Volo.Abp.Http.Modeling; + +namespace Volo.Abp.AspNetCore.Mvc.Conventions +{ + public class ConventionalControllerOptions + { + public ConventionalControllerSettingList ConventionalControllerSettings { get; } + + public List FormBodyBindingIgnoredTypes { get; } + + public ConventionalControllerOptions() + { + ConventionalControllerSettings = new ConventionalControllerSettingList(); + + FormBodyBindingIgnoredTypes = new List + { + typeof(IFormFile) + }; + } + + public ConventionalControllerOptions Create(Assembly assembly, [CanBeNull] Action optionsAction = null) + { + var setting = new ConventionalControllerSetting(assembly, ModuleApiDescriptionModel.DefaultRootPath); + optionsAction?.Invoke(setting); + setting.Initialize(); + ConventionalControllerSettings.Add(setting); + return this; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/ConventionalControllerSetting.cs similarity index 92% rename from src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs rename to src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/ConventionalControllerSetting.cs index a86d5d920f..10ba9b0912 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpControllerAssemblySetting.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/ConventionalControllerSetting.cs @@ -9,9 +9,9 @@ using Microsoft.AspNetCore.Mvc.Versioning; using Volo.Abp.Application.Services; using Volo.Abp.Reflection; -namespace Volo.Abp.AspNetCore.Mvc +namespace Volo.Abp.AspNetCore.Mvc.Conventions { - public class AbpControllerAssemblySetting + public class ConventionalControllerSetting { [NotNull] public Assembly Assembly { get; } @@ -47,7 +47,7 @@ namespace Volo.Abp.AspNetCore.Mvc public Action ApiVersionConfigurer { get; set; } - public AbpControllerAssemblySetting([NotNull] Assembly assembly, [NotNull] string rootPath) + public ConventionalControllerSetting([NotNull] Assembly assembly, [NotNull] string rootPath) { Check.NotNull(assembly, rootPath); diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ControllerAssemblySettingList.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/ConventionalControllerSettingList.cs similarity index 55% rename from src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ControllerAssemblySettingList.cs rename to src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/ConventionalControllerSettingList.cs index b59ac8c576..a460a8fe00 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ControllerAssemblySettingList.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/ConventionalControllerSettingList.cs @@ -3,12 +3,12 @@ using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; -namespace Volo.Abp.AspNetCore.Mvc +namespace Volo.Abp.AspNetCore.Mvc.Conventions { - public class ControllerAssemblySettingList : List + public class ConventionalControllerSettingList : List { [CanBeNull] - public AbpControllerAssemblySetting GetSettingOrNull(Type controllerType) + public ConventionalControllerSetting GetSettingOrNull(Type controllerType) { return this.FirstOrDefault(controllerSetting => controllerSetting.ControllerTypes.Contains(controllerType)); } diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/IAbpServiceConvention.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/IAbpServiceConvention.cs new file mode 100644 index 0000000000..aac601d0b4 --- /dev/null +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/IAbpServiceConvention.cs @@ -0,0 +1,8 @@ +using Microsoft.AspNetCore.Mvc.ApplicationModels; + +namespace Volo.Abp.AspNetCore.Mvc.Conventions +{ + public interface IAbpServiceConvention : IApplicationModelConvention + { + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/UrlActionNameNormalizerContext.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/UrlActionNameNormalizerContext.cs similarity index 93% rename from src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/UrlActionNameNormalizerContext.cs rename to src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/UrlActionNameNormalizerContext.cs index 631b8b50de..ebc122af05 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/UrlActionNameNormalizerContext.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/UrlActionNameNormalizerContext.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Mvc.ApplicationModels; -namespace Volo.Abp.AspNetCore.Mvc +namespace Volo.Abp.AspNetCore.Mvc.Conventions { public class UrlActionNameNormalizerContext { diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/UrlControllerNameNormalizerContext.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/UrlControllerNameNormalizerContext.cs similarity index 87% rename from src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/UrlControllerNameNormalizerContext.cs rename to src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/UrlControllerNameNormalizerContext.cs index aad40fe4d7..2cb3bdc733 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/UrlControllerNameNormalizerContext.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/UrlControllerNameNormalizerContext.cs @@ -1,4 +1,4 @@ -namespace Volo.Abp.AspNetCore.Mvc +namespace Volo.Abp.AspNetCore.Mvc.Conventions { public class UrlControllerNameNormalizerContext { diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/IAbpAppServiceConvention.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/IAbpAppServiceConvention.cs deleted file mode 100644 index 899d66b4c2..0000000000 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/IAbpAppServiceConvention.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Microsoft.AspNetCore.Mvc.ApplicationModels; - -namespace Volo.Abp.AspNetCore.Mvc -{ - public interface IAbpAppServiceConvention : IApplicationModelConvention - { - } -} \ No newline at end of file diff --git a/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs b/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs index b826251fa2..db1f278d6b 100644 --- a/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs +++ b/src/Volo.Abp.Identity.HttpApi.Host/AbpIdentityHttpApiHostModule.cs @@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.EntityFrameworkCore; -using Microsoft.Examples; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -90,12 +89,12 @@ namespace Volo.Abp.Identity.HttpApi.Host services.Configure(options => { - options.AppServiceControllers.Create(typeof(AbpIdentityHttpApiHostModule).Assembly, o => + options.ConventionalControllers.Create(typeof(AbpIdentityHttpApiHostModule).Assembly, o => { o.TypePredicate = t => t == typeof(CallsController); }); - options.AppServiceControllers.Create(typeof(AbpIdentityHttpApiHostModule).Assembly, o => + options.ConventionalControllers.Create(typeof(AbpIdentityHttpApiHostModule).Assembly, o => { o.TypePredicate = t => t == typeof(Host.VersioningTests.V2.CallsController); o.RootPath = "app/compat"; diff --git a/src/Volo.Abp.Identity.HttpApi.Host/SwaggerDefaultValues.cs b/src/Volo.Abp.Identity.HttpApi.Host/SwaggerDefaultValues.cs index 11045d85a6..f273dced70 100644 --- a/src/Volo.Abp.Identity.HttpApi.Host/SwaggerDefaultValues.cs +++ b/src/Volo.Abp.Identity.HttpApi.Host/SwaggerDefaultValues.cs @@ -1,9 +1,9 @@ -namespace Microsoft.Examples -{ - using Swashbuckle.AspNetCore.Swagger; - using Swashbuckle.AspNetCore.SwaggerGen; - using System.Linq; +using System.Linq; +using Swashbuckle.AspNetCore.Swagger; +using Swashbuckle.AspNetCore.SwaggerGen; +namespace Volo.Abp.Identity.HttpApi.Host +{ /// /// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter. /// diff --git a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs index 195ce009b7..5f9c285c9f 100644 --- a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs +++ b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/AbpIdentityHttpApiModule.cs @@ -15,14 +15,14 @@ namespace Volo.Abp.Identity services.Configure(options => { - options.AppServiceControllers.Create(typeof(AbpIdentityApplicationModule).Assembly, opts => + options.ConventionalControllers.Create(typeof(AbpIdentityApplicationModule).Assembly, opts => { opts.RootPath = "identity"; opts.UrlControllerNameNormalizer = context => context.ControllerName.RemovePreFix("Identity"); opts.ApiVersions.Add(new ApiVersion(2, 0)); }); - options.AppServiceControllers.Create(typeof(AbpIdentityHttpApiModule).Assembly); + options.ConventionalControllers.Create(typeof(AbpIdentityHttpApiModule).Assembly); }); } } diff --git a/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs b/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs index 15b8727063..5e2e8e2986 100644 --- a/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs +++ b/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs @@ -24,7 +24,7 @@ namespace Volo.Abp.AspNetCore.Mvc services.Configure(options => { - options.AppServiceControllers.Create(typeof(TestAppModule).Assembly, opts => + options.ConventionalControllers.Create(typeof(TestAppModule).Assembly, opts => { opts.UrlActionNameNormalizer = context => string.Equals(context.ActionNameInUrl, "phone", StringComparison.OrdinalIgnoreCase) diff --git a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs index 68a50f8416..ad035e8edd 100644 --- a/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs +++ b/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs @@ -25,14 +25,14 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning services.Configure(options => { //2.0 Version - options.AppServiceControllers.Create(typeof(AbpAspNetCoreMvcVersioningTestModule).Assembly, opts => + options.ConventionalControllers.Create(typeof(AbpAspNetCoreMvcVersioningTestModule).Assembly, opts => { opts.TypePredicate = t => t.Namespace == typeof(Volo.Abp.AspNetCore.Mvc.Versioning.App.TodoAppService).Namespace; opts.ApiVersions.Add(new ApiVersion(2, 0)); }); //1.0 Compatability version - options.AppServiceControllers.Create(typeof(AbpAspNetCoreMvcVersioningTestModule).Assembly, opts => + options.ConventionalControllers.Create(typeof(AbpAspNetCoreMvcVersioningTestModule).Assembly, opts => { opts.TypePredicate = t => t.Namespace == typeof(Volo.Abp.AspNetCore.Mvc.Versioning.App.Compat.TodoAppService).Namespace; opts.ApiVersions.Add(new ApiVersion(1, 0)); From b6f23379db27c834c76aab6796ff2e4036eacf65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 6 Oct 2017 10:46:41 +0300 Subject: [PATCH 20/23] Use full namespace for js proxies. --- .../JQuery/JQueryProxyScriptGenerator.cs | 38 ++++++++++++++----- .../Generators/ProxyScriptingJsFuncHelper.cs | 2 +- .../Volo/Abp/Identity/FixtureController.cs | 7 ++++ .../Abp/Identity/V4/FixtureV4Controller.cs | 26 +++++++++++++ 4 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/V4/FixtureV4Controller.cs diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs b/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs index 8310d286ed..d910109caa 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs @@ -1,5 +1,8 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Text; +using Volo.Abp.Application.Services; using Volo.Abp.DependencyInjection; using Volo.Abp.Http.Modeling; @@ -20,8 +23,6 @@ namespace Volo.Abp.Http.ProxyScripting.Generators.JQuery script.AppendLine("/* This file is automatically generated by ABP framework to use MVC Controllers from javascript. */"); script.AppendLine(); - script.AppendLine("var abp = abp || {};"); - script.AppendLine("abp.services = abp.services || {};"); foreach (var module in model.Modules.Values) { @@ -37,9 +38,9 @@ namespace Volo.Abp.Http.ProxyScripting.Generators.JQuery //TODO: Eleminate repeating module.RootPath.Replace("/", ".").ToCamelCase() ! //TODO: Remove illegal chars (like '-') from module/controller names! - script.AppendLine($"// module '{module.RootPath.ToCamelCase()}'"); + script.AppendLine($"// module {module.RootPath.ToCamelCase()}"); + script.AppendLine(); script.AppendLine("(function(){"); - script.AppendLine($"abp.utils.createNamespace(abp, 'services.{module.RootPath.Replace("/", ".").ToCamelCase()}');"); foreach (var controller in module.Controllers.Values) { @@ -53,28 +54,30 @@ namespace Volo.Abp.Http.ProxyScripting.Generators.JQuery private static void AddControllerScript(StringBuilder script, ModuleApiDescriptionModel module, ControllerApiDescriptionModel controller) { - script.AppendLine($" // controller '{controller.ControllerName.ToCamelCase()}'"); + var controllerName = GetNormalizedTypeName(controller.TypeAsString); + + script.AppendLine($" // controller {controllerName}"); + script.AppendLine(); script.AppendLine(" (function(){"); script.AppendLine(); - script.AppendLine($" abp.services.{module.RootPath.Replace("/", ".").ToCamelCase()}.{controller.ControllerName.ToCamelCase()} = abp.services.{module.RootPath.Replace("/", ".").ToCamelCase()}.{controller.ControllerName.ToCamelCase()} || {{}};"); + script.AppendLine($" abp.utils.createNamespace(window, '{controllerName}');"); foreach (var action in controller.Actions.Values) { script.AppendLine(); - AddActionScript(script, module, controller, action); + AddActionScript(script, module, controllerName, controller, action); } script.AppendLine(); script.AppendLine(" })();"); } - private static void AddActionScript(StringBuilder script, ModuleApiDescriptionModel module, ControllerApiDescriptionModel controller, ActionApiDescriptionModel action) + private static void AddActionScript(StringBuilder script, ModuleApiDescriptionModel module, string controllerName, ControllerApiDescriptionModel controller, ActionApiDescriptionModel action) { var parameterList = ProxyScriptingJsFuncHelper.GenerateJsFuncParameterList(action, "ajaxParams"); - script.AppendLine($" // action '{action.Name.ToCamelCase()}'"); - script.AppendLine($" abp.services.{module.RootPath.Replace("/", ".").ToCamelCase()}.{controller.ControllerName.ToCamelCase()}{ProxyScriptingJsFuncHelper.WrapWithBracketsOrWithDotPrefix(action.Name.RemovePostFix("Async").ToCamelCase())} = function({parameterList}) {{"); + script.AppendLine($" {controllerName}{ProxyScriptingJsFuncHelper.WrapWithBracketsOrWithDotPrefix(action.Name.RemovePostFix("Async").ToCamelCase())} = function({parameterList}) {{"); script.AppendLine(" return abp.ajax($.extend(true, {"); AddAjaxCallParameters(script, controller, action); @@ -121,5 +124,20 @@ namespace Volo.Abp.Http.ProxyScripting.Generators.JQuery script.AppendLine(); } + + private static string GetNormalizedTypeName(string typeWithAssemblyName) + { + return CamelCaseWithNamespace( + typeWithAssemblyName.Split(",")[0] + .Trim() + .RemovePostFix(ApplicationService.CommonPostfixes) + .RemovePostFix("Controller") + ); + } + + private static string CamelCaseWithNamespace(string name) + { + return name.Split('.').Select(n => n.ToCamelCase()).JoinAsString("."); + } } } \ No newline at end of file diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/ProxyScriptingJsFuncHelper.cs b/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/ProxyScriptingJsFuncHelper.cs index a92bbe1710..baa52f3155 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/ProxyScriptingJsFuncHelper.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/ProxyScriptingJsFuncHelper.cs @@ -133,7 +133,7 @@ namespace Volo.Abp.Http.ProxyScripting.Generators public static string GenerateJsFuncParameterList(ActionApiDescriptionModel action, string ajaxParametersName) { - var methodParamNames = action.Parameters.Select(p => p.NameOnMethod).Distinct().ToList(); + var methodParamNames = action.ParametersOnMethod.Select(p => p.Name).Distinct().ToList(); methodParamNames.Add(ajaxParametersName); return methodParamNames.Select(prmName => NormalizeJsVariableName(prmName.ToCamelCase())).JoinAsString(", "); } diff --git a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs index 7826bc5e7b..552573e1d7 100644 --- a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs +++ b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs @@ -35,5 +35,12 @@ namespace Volo.Abp.Identity { return 43; } + + [HttpPost] + [Route("{id}")] + public int Post(int id) + { + return id; + } } } \ No newline at end of file diff --git a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/V4/FixtureV4Controller.cs b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/V4/FixtureV4Controller.cs new file mode 100644 index 0000000000..a5a3fa5655 --- /dev/null +++ b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/V4/FixtureV4Controller.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.ApiVersioning; +using Volo.Abp.Application.Services; +using Volo.Abp.AspNetCore.Mvc; + +namespace Volo.Abp.Identity.V4 +{ + //TODO: This is just a test controller and will be removed later + [ApiVersion("4.0")] + [Route("api/v{api-version:apiVersion}/identity/fixture")] + public class FixtureController : AbpController, IRemoteService + { + private readonly IRequestedApiVersion _requestedApiVersion; + + public FixtureController(IRequestedApiVersion requestedApiVersion) + { + _requestedApiVersion = requestedApiVersion; + } + + [HttpGet] + public string Get() + { + return 41 + " - " + _requestedApiVersion.Current; + } + } +} \ No newline at end of file From d51df64b3ad131d6a7719bd396dabf75ff4170b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 6 Oct 2017 12:37:13 +0300 Subject: [PATCH 21/23] Method overloading support for jquery script generation. --- .../AspNetCoreApiDescriptionModelProvider.cs | 1 + .../Modeling/ActionApiDescriptionModel.cs | 6 ++- .../JQuery/JQueryProxyScriptGenerator.cs | 37 +++++++++++++++++-- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs index 4ddfa24f45..1d1dc7d252 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs @@ -80,6 +80,7 @@ namespace Volo.Abp.AspNetCore.Mvc } var actionModel = controllerModel.AddAction(uniqueMethodName, ActionApiDescriptionModel.Create( + uniqueMethodName, method, apiDescription.RelativePath, apiDescription.HttpMethod, diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs index 86d903f97f..7e9d375d06 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs @@ -10,6 +10,8 @@ namespace Volo.Abp.Http.Modeling [Serializable] public class ActionApiDescriptionModel { + public string UniqueName { get; set; } + public string Name { get; set; } public string HttpMethod { get; set; } @@ -29,8 +31,9 @@ namespace Volo.Abp.Http.Modeling } - public static ActionApiDescriptionModel Create([NotNull] MethodInfo method, [NotNull] string url, [NotNull] string httpMethod, [NotNull] IList supportedVersions) + public static ActionApiDescriptionModel Create([NotNull] string uniqueName, [NotNull] MethodInfo method, [NotNull] string url, [NotNull] string httpMethod, [NotNull] IList supportedVersions) { + Check.NotNull(uniqueName, nameof(uniqueName)); Check.NotNull(method, nameof(method)); Check.NotNull(url, nameof(url)); Check.NotNull(httpMethod, nameof(httpMethod)); @@ -38,6 +41,7 @@ namespace Volo.Abp.Http.Modeling return new ActionApiDescriptionModel { + UniqueName = uniqueName, Name = method.Name, Url = url, HttpMethod = httpMethod, diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs b/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs index d910109caa..00aceb6c0b 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs @@ -63,21 +63,23 @@ namespace Volo.Abp.Http.ProxyScripting.Generators.JQuery script.AppendLine($" abp.utils.createNamespace(window, '{controllerName}');"); + var normalizedActionNames = CalculateNormalizedActionNames(controller.Actions); + foreach (var action in controller.Actions.Values) { script.AppendLine(); - AddActionScript(script, module, controllerName, controller, action); + AddActionScript(script, module, controllerName, controller, action, normalizedActionNames[action]); } script.AppendLine(); script.AppendLine(" })();"); } - private static void AddActionScript(StringBuilder script, ModuleApiDescriptionModel module, string controllerName, ControllerApiDescriptionModel controller, ActionApiDescriptionModel action) + private static void AddActionScript(StringBuilder script, ModuleApiDescriptionModel module, string controllerName, ControllerApiDescriptionModel controller, ActionApiDescriptionModel action, string normalizedActionName) { var parameterList = ProxyScriptingJsFuncHelper.GenerateJsFuncParameterList(action, "ajaxParams"); - script.AppendLine($" {controllerName}{ProxyScriptingJsFuncHelper.WrapWithBracketsOrWithDotPrefix(action.Name.RemovePostFix("Async").ToCamelCase())} = function({parameterList}) {{"); + script.AppendLine($" {controllerName}{ProxyScriptingJsFuncHelper.WrapWithBracketsOrWithDotPrefix(normalizedActionName.RemovePostFix("Async").ToCamelCase())} = function({parameterList}) {{"); script.AppendLine(" return abp.ajax($.extend(true, {"); AddAjaxCallParameters(script, controller, action); @@ -125,6 +127,35 @@ namespace Volo.Abp.Http.ProxyScripting.Generators.JQuery script.AppendLine(); } + private static Dictionary CalculateNormalizedActionNames(Dictionary actions) + { + var result = new Dictionary(); + + var actionsByName = new Dictionary>(); + + foreach (var action in actions.Values) + { + var actionName = action.Name.RemovePostFix("Async").ToCamelCase(); + result[action] = actionName; + actionsByName.GetOrAdd(actionName, () => new List()).Add(action); + } + + foreach (var actionByName in actionsByName) + { + if (actionByName.Value.Count <= 1) + { + continue; + } + + foreach (var action in actionByName.Value) + { + result[action] = action.UniqueName; + } + } + + return result; + } + private static string GetNormalizedTypeName(string typeWithAssemblyName) { return CamelCaseWithNamespace( From d62424ebe31a4dea8b7ed9cab046ffffa14c36ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 6 Oct 2017 12:40:55 +0300 Subject: [PATCH 22/23] Refactor: Remove unused parameters --- .../Generators/JQuery/JQueryProxyScriptGenerator.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs b/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs index 00aceb6c0b..d9fcb8559c 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs @@ -45,14 +45,14 @@ namespace Volo.Abp.Http.ProxyScripting.Generators.JQuery foreach (var controller in module.Controllers.Values) { script.AppendLine(); - AddControllerScript(script, module, controller); + AddControllerScript(script, controller); } script.AppendLine(); script.AppendLine("})();"); } - private static void AddControllerScript(StringBuilder script, ModuleApiDescriptionModel module, ControllerApiDescriptionModel controller) + private static void AddControllerScript(StringBuilder script, ControllerApiDescriptionModel controller) { var controllerName = GetNormalizedTypeName(controller.TypeAsString); @@ -68,27 +68,27 @@ namespace Volo.Abp.Http.ProxyScripting.Generators.JQuery foreach (var action in controller.Actions.Values) { script.AppendLine(); - AddActionScript(script, module, controllerName, controller, action, normalizedActionNames[action]); + AddActionScript(script, controllerName, action, normalizedActionNames[action]); } script.AppendLine(); script.AppendLine(" })();"); } - private static void AddActionScript(StringBuilder script, ModuleApiDescriptionModel module, string controllerName, ControllerApiDescriptionModel controller, ActionApiDescriptionModel action, string normalizedActionName) + private static void AddActionScript(StringBuilder script, string controllerName, ActionApiDescriptionModel action, string normalizedActionName) { var parameterList = ProxyScriptingJsFuncHelper.GenerateJsFuncParameterList(action, "ajaxParams"); script.AppendLine($" {controllerName}{ProxyScriptingJsFuncHelper.WrapWithBracketsOrWithDotPrefix(normalizedActionName.RemovePostFix("Async").ToCamelCase())} = function({parameterList}) {{"); script.AppendLine(" return abp.ajax($.extend(true, {"); - AddAjaxCallParameters(script, controller, action); + AddAjaxCallParameters(script, action); script.AppendLine(" }, ajaxParams));;"); script.AppendLine(" };"); } - private static void AddAjaxCallParameters(StringBuilder script, ControllerApiDescriptionModel controller, ActionApiDescriptionModel action) + private static void AddAjaxCallParameters(StringBuilder script, ActionApiDescriptionModel action) { var httpMethod = action.HttpMethod?.ToUpperInvariant() ?? "POST"; From c3f77c1229508279015054a9b4f5586404a88a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 6 Oct 2017 14:01:51 +0300 Subject: [PATCH 23/23] Finalized jquery proxy script generation. --- .../AspNetCoreApiDescriptionModelProvider.cs | 2 +- .../DynamicHttpProxyInterceptor.cs | 4 +-- .../JQuery/JQueryProxyScriptGenerator.cs | 28 +++++++++++++++++++ .../VersioningTests/V1/CallDto.cs | 9 ++++++ .../VersioningTests/V1/CallsController.cs | 8 +----- .../VersioningTests/V2/Calls2Controller.cs | 2 +- .../Volo/Abp/Identity/FixtureController.cs | 2 +- .../Abp/Identity/V4/FixtureV4Controller.cs | 2 +- 8 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V1/CallDto.cs diff --git a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs index 1d1dc7d252..156e4114ae 100644 --- a/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs +++ b/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs @@ -84,7 +84,7 @@ namespace Volo.Abp.AspNetCore.Mvc method, apiDescription.RelativePath, apiDescription.HttpMethod, - setting?.ApiVersions.Select(v => v.ToString()).ToList() ?? new List() + setting?.ApiVersions.Select(v => v.ToString()).ToList() ?? new List() //TODO: Also get from ApiVersion attributes if available..? )); AddParameterDescriptionsToModel(actionModel, method, apiDescription); diff --git a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs index dee7b66238..21f36577d8 100644 --- a/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs +++ b/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs @@ -132,8 +132,8 @@ namespace Volo.Abp.Http.Client.DynamicProxying { var apiVersion = FindBestApiVersion(action); - //TODO: Make names configurable! - var versionParam = action.Parameters.FirstOrDefault(p => p.Name == "apiVersion") ?? + //TODO: Make names configurable? + var versionParam = action.Parameters.FirstOrDefault(p => p.Name == "apiVersion" && p.BindingSourceId == ParameterBindingSources.Path) ?? action.Parameters.FirstOrDefault(p => p.Name == "api-version" && p.BindingSourceId == ParameterBindingSources.Query); return new ApiVersionInfo(versionParam?.BindingSourceId, apiVersion); diff --git a/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs b/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs index d9fcb8559c..5c399ab83a 100644 --- a/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs +++ b/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs @@ -80,6 +80,16 @@ namespace Volo.Abp.Http.ProxyScripting.Generators.JQuery var parameterList = ProxyScriptingJsFuncHelper.GenerateJsFuncParameterList(action, "ajaxParams"); script.AppendLine($" {controllerName}{ProxyScriptingJsFuncHelper.WrapWithBracketsOrWithDotPrefix(normalizedActionName.RemovePostFix("Async").ToCamelCase())} = function({parameterList}) {{"); + + var versionParam = action.Parameters.FirstOrDefault(p => p.Name == "apiVersion" && p.BindingSourceId == ParameterBindingSources.Path) ?? + action.Parameters.FirstOrDefault(p => p.Name == "api-version" && p.BindingSourceId == ParameterBindingSources.Query); + + if (versionParam != null) + { + var version = FindBestApiVersion(action); + script.AppendLine($" var {ProxyScriptingJsFuncHelper.NormalizeJsVariableName(versionParam.Name)} = '{version}';"); + } + script.AppendLine(" return abp.ajax($.extend(true, {"); AddAjaxCallParameters(script, action); @@ -88,6 +98,24 @@ namespace Volo.Abp.Http.ProxyScripting.Generators.JQuery script.AppendLine(" };"); } + private static string FindBestApiVersion(ActionApiDescriptionModel action) + { + //var configuredVersion = GetConfiguredApiVersion(); //TODO: Implement + string configuredVersion = null; + + if (action.SupportedVersions.IsNullOrEmpty()) + { + return configuredVersion ?? "1.0"; + } + + if (action.SupportedVersions.Contains(configuredVersion)) + { + return configuredVersion; + } + + return action.SupportedVersions.Last(); //TODO: Ensure to get the latest version! + } + private static void AddAjaxCallParameters(StringBuilder script, ActionApiDescriptionModel action) { var httpMethod = action.HttpMethod?.ToUpperInvariant() ?? "POST"; diff --git a/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V1/CallDto.cs b/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V1/CallDto.cs new file mode 100644 index 0000000000..b8aa84f150 --- /dev/null +++ b/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V1/CallDto.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Application.Dtos; + +namespace Volo.Abp.Identity.HttpApi.Host.VersioningTests.V1 +{ + public class CallDto : EntityDto + { + public string Number { get; set; } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V1/CallsController.cs b/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V1/CallsController.cs index 5837df2e2b..a61d3ca4a2 100644 --- a/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V1/CallsController.cs +++ b/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V1/CallsController.cs @@ -1,13 +1,12 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; -using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; using Volo.Abp.AspNetCore.Mvc; namespace Volo.Abp.Identity.HttpApi.Host.VersioningTests.V1 { [ApiVersion("1.0")] - [Route("api/v{api-version:apiVersion}/calls")] + [Route("api/v{apiVersion:apiVersion}/calls")] public class CallsController : AbpController, IRemoteService { private static readonly List Calls = new List @@ -22,9 +21,4 @@ namespace Volo.Abp.Identity.HttpApi.Host.VersioningTests.V1 return Calls; } } - - public class CallDto : EntityDto - { - public string Number { get; set; } - } } diff --git a/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V2/Calls2Controller.cs b/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V2/Calls2Controller.cs index 4a7b908782..808d177c00 100644 --- a/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V2/Calls2Controller.cs +++ b/src/Volo.Abp.Identity.HttpApi.Host/VersioningTests/V2/Calls2Controller.cs @@ -8,7 +8,7 @@ using Volo.Abp.Identity.HttpApi.Host.VersioningTests.V1; namespace Volo.Abp.Identity.HttpApi.Host.VersioningTests.V2 { [ApiVersion("2.0")] - [Route("api/v{api-version:apiVersion}/calls")] + [Route("api/v{apiVersion:apiVersion}/calls")] public class CallsController : AbpController, IRemoteService { private static List _calls = new List diff --git a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs index 552573e1d7..9321177b8a 100644 --- a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs +++ b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/FixtureController.cs @@ -8,7 +8,7 @@ namespace Volo.Abp.Identity //TODO: This is just a test controller and will be removed later [ApiVersion("3.0")] [ApiVersion("2.0", Deprecated = true)] - [Route("api/v{api-version:apiVersion}/identity/fixture")] + [Route("api/v{apiVersion:apiVersion}/identity/fixture")] public class FixtureController : AbpController, IRemoteService { private readonly IRequestedApiVersion _requestedApiVersion; diff --git a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/V4/FixtureV4Controller.cs b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/V4/FixtureV4Controller.cs index a5a3fa5655..dc271bdbba 100644 --- a/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/V4/FixtureV4Controller.cs +++ b/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/V4/FixtureV4Controller.cs @@ -7,7 +7,7 @@ namespace Volo.Abp.Identity.V4 { //TODO: This is just a test controller and will be removed later [ApiVersion("4.0")] - [Route("api/v{api-version:apiVersion}/identity/fixture")] + [Route("api/v{apiVersion:apiVersion}/identity/fixture")] public class FixtureController : AbpController, IRemoteService { private readonly IRequestedApiVersion _requestedApiVersion;