Browse Source

Merge branch 'abpframework:dev' into dev

pull/10981/head
Erfan Shokrollahi 5 years ago
committed by GitHub
parent
commit
a0fa0f0d84
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/ar.json
  2. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/cs.json
  3. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/de-DE.json
  4. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en-GB.json
  5. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json
  6. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/es.json
  7. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/fi.json
  8. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/fr.json
  9. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/hi.json
  10. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/hu.json
  11. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/is.json
  12. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/it.json
  13. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/nl.json
  14. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/pl-PL.json
  15. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/pt-BR.json
  16. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/ro-RO.json
  17. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/ru.json
  18. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/sk.json
  19. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/sl.json
  20. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/tr.json
  21. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/vi.json
  22. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/zh-Hans.json
  23. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/zh-Hant.json
  24. 9
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  25. 349
      docs/en/API/API-Versioning.md
  26. 98
      docs/en/Community-Articles/2022-02-06-How-to-Hide-ABP-Related-Endpoints-on-Swagger-UI/POST.md
  27. BIN
      docs/en/Community-Articles/2022-02-06-How-to-Hide-ABP-Related-Endpoints-on-Swagger-UI/swagger-document-filter.png
  28. 4
      docs/en/docs-nav.json
  29. 347
      docs/zh-Hans/API/API-Versioning.md
  30. 4
      docs/zh-Hans/docs-nav.json
  31. 3
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs
  32. 34
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiDescriptionExtensions.cs
  33. 27
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs
  34. 17
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs
  35. 2
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs
  36. 2
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Versioning/HttpContextRequestedApiVersion.cs
  37. 2
      framework/src/Volo.Abp.Http.Client.Web/Volo/Abp/Http/Client/Web/Conventions/AbpHttpClientProxyServiceConvention.cs
  38. 10
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs
  39. 22
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/CurrentApiVersionInfo.cs
  40. 10
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ICurrentApiVersionInfo.cs
  41. 8
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs
  42. 5
      framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ModuleApiDescriptionModel.cs
  43. 2
      framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs
  44. 2
      npm/packs/core/src/abp.js

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/ar.json

@ -10,6 +10,7 @@
"CommercialHomePage": "الصفحة الرئيسية للنظام التجاري",
"CommercialSupportWebSite": "موقع الدعم الفني للنظام التجاري",
"CommunityWebSite": "موقع مجتمع ABP",
"ManageAccount": "حسابي | ABP.IO"
"ManageAccount": "حسابي | ABP.IO",
"ManageYourAccount": "إدارة حسابك"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/cs.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Obchodní domovská stránka",
"CommercialSupportWebSite": "Webová stránka komerční podpory",
"CommunityWebSite": "Web komunity ABP",
"ManageAccount": "Můj účet | ABP.IO"
"ManageAccount": "Můj účet | ABP.IO",
"ManageYourAccount": "Správa vášho účtu"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/de-DE.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Commercial Website",
"CommercialSupportWebSite": "Commercial Support-Website",
"CommunityWebSite": "ABP Community-Website",
"ManageAccount": "Mein Konto | ABP.IO"
"ManageAccount": "Mein Konto | ABP.IO",
"ManageYourAccount": "Verwalten Sie Ihr Konto"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en-GB.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Commercial home page",
"CommercialSupportWebSite": "Commercial support web site",
"CommunityWebSite": "ABP community web site",
"ManageAccount": "My Account | ABP.IO"
"ManageAccount": "My Account | ABP.IO",
"ManageYourProfile": "Manage your profile"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Commercial home page",
"CommercialSupportWebSite": "Commercial support web site",
"CommunityWebSite": "ABP community web site",
"ManageAccount": "My Account | ABP.IO"
"ManageAccount": "My Account | ABP.IO",
"ManageYourProfile": "Manage your profile"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/es.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Página de inicio comercial",
"CommercialSupportWebSite": "Sitio web de soporte comercial",
"CommunityWebSite": "Sitio web comunidad ABP",
"ManageAccount": "Mi cuenta | ABP.IO"
"ManageAccount": "Mi cuenta | ABP.IO",
"ManageYourAccount": "Administra tu cuenta"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/fi.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Kaupallinen kotisivu",
"CommercialSupportWebSite": "Kaupallisen tuen verkkosivusto",
"CommunityWebSite": "ABP-yhteisön verkkosivusto",
"ManageAccount": "Oma tili | ABP.IO"
"ManageAccount": "Oma tili | ABP.IO",
"ManageYourAccount": "Hallitse tiliäsi"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/fr.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Page d'accueil commerciale",
"CommercialSupportWebSite": "Site Web de support commercial",
"CommunityWebSite": "Site Web de la communauté ABP",
"ManageAccount": "Mon compte | ABP.IO"
"ManageAccount": "Mon compte | ABP.IO",
"ManageYourAccount": "Gérer votre compte"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/hi.json

@ -10,6 +10,7 @@
"CommercialHomePage": "वाणिज्यिक मुख पृष्ठ",
"CommercialSupportWebSite": "वाणिज्यिक समर्थन वेब साइट",
"CommunityWebSite": "एबीपी सामुदायिक वेब साइट",
"ManageAccount": "मेरा खाता | एबीपी.आईओ"
"ManageAccount": "मेरा खाता | एबीपी.आईओ",
"ManageYourAccount": "अपने खाते को प्रबंधित करें"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/hu.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Kereskedelmi honlap",
"CommercialSupportWebSite": "Kereskedelmi támogatási webhely",
"CommunityWebSite": "ABP közösségi webhely",
"ManageAccount": "Saját fiók | ABP.IO"
"ManageAccount": "Saját fiók | ABP.IO",
"ManageYourAccount": "Fiók kezelése"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/is.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Heimasíða Commercial útgáfunnarinnar",
"CommercialSupportWebSite": "Þjónustu síða Commercial útgáfunnarinnar",
"CommunityWebSite": "Samfélagssíða ABP",
"ManageAccount": "Minn aðgangur | ABP.IO"
"ManageAccount": "Minn aðgangur | ABP.IO",
"ManageYourAccount": "Stjórnaðu aðganginn þinn"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/it.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Home page di Commercial",
"CommercialSupportWebSite": "Sito web supporto di Commercial",
"CommunityWebSite": "Sito web della comunità ABP",
"ManageAccount": "Il mio account | ABP.IO"
"ManageAccount": "Il mio account | ABP.IO",
"ManageYourAccount": "Gestisci il tuo account"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/nl.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Commerciële startpagina",
"CommercialSupportWebSite": "Commerciële ondersteuningswebsite",
"CommunityWebSite": "ABP-communitywebsite",
"ManageAccount": "Mijn account | ABP.IO"
"ManageAccount": "Mijn account | ABP.IO",
"ManageYourAccount": "Beheer uw account"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/pl-PL.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Komercyjna strona główna",
"CommercialSupportWebSite": "Witryna wsparcia komercyjnego",
"CommunityWebSite": "Strona społeczności ABP",
"ManageAccount": "Moje konto | ABP.IO"
"ManageAccount": "Moje konto | ABP.IO",
"ManageYourAccount": "Zarządzaj swoim kontem"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/pt-BR.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Página inicial comercial",
"CommercialSupportWebSite": "Site de suporte comercial",
"CommunityWebSite": "Site da comunidade ABP",
"ManageAccount": "Minha conta | ABP.IO"
"ManageAccount": "Minha conta | ABP.IO",
"ManageYourAccount": "Gerenciar sua conta"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/ro-RO.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Pagina principală comercială",
"CommercialSupportWebSite": "Site-ul web al suportului comercial",
"CommunityWebSite": "Site-ul web al comunităţii ABP",
"ManageAccount": "Contul meu | ABP.IO"
"ManageAccount": "Contul meu | ABP.IO",
"ManageYourAccount": "Gestionaţi contul dvs."
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/ru.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Главная страница коммерческой версии",
"CommercialSupportWebSite": "Сайт коммерческой поддержки",
"CommunityWebSite": "Веб-сайт сообщества ABP",
"ManageAccount": "Моя учетная запись | ABP.IO"
"ManageAccount": "Моя учетная запись | ABP.IO",
"ManageYourAccount": "Управление вашей учетной записью"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/sk.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Komerčná domovská stránka",
"CommercialSupportWebSite": "Webová stránka komerčnej podpory",
"CommunityWebSite": "Webová stránka ABP komunity",
"ManageAccount": "Môj účet | ABP.IO"
"ManageAccount": "Môj účet | ABP.IO",
"ManageYourAccount": "Môj účet"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/sl.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Domača stran različice Commercial",
"CommercialSupportWebSite": "Spletna stran za komercialno podporo",
"CommunityWebSite": "Spletno mesto skupnosti ABP",
"ManageAccount": "Moj račun | ABP.IO"
"ManageAccount": "Moj račun | ABP.IO",
"ManageYourAccount": "Upravljanje svojega računa"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/tr.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Kurumsal ana sayfa",
"CommercialSupportWebSite": "Kurumsal destek web sitesi",
"CommunityWebSite": "ABP topluluk web sitesi",
"ManageAccount": "Hesabım | ABP.IO"
"ManageAccount": "Hesabım | ABP.IO",
"ManageYourProfile": "Hesabımı yönet"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/vi.json

@ -10,6 +10,7 @@
"CommercialHomePage": "Trang chủ thương mại",
"CommercialSupportWebSite": "Trang web hỗ trợ thương mại",
"CommunityWebSite": "Trang web cộng đồng ABP",
"ManageAccount": "Tài khoản của tôi | ABP.IO"
"ManageAccount": "Tài khoản của tôi | ABP.IO",
"ManageYourAccount": "Quản lý tài khoản của bạn"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/zh-Hans.json

@ -10,6 +10,7 @@
"CommercialHomePage": "商业版首页",
"CommercialSupportWebSite": "商业版支持网站",
"CommunityWebSite": "ABP社区网站",
"ManageAccount": "我的帐户 | ABP.IO"
"ManageAccount": "我的帐户 | ABP.IO",
"ManageYourAccount": "管理您的帐户"
}
}

3
abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/zh-Hant.json

@ -10,6 +10,7 @@
"CommercialHomePage": "商業版首頁",
"CommercialSupportWebSite": "商業版支援網站",
"CommunityWebSite": "ABP社區網站",
"ManageAccount": "我的帳戶 | ABP.IO"
"ManageAccount": "我的帳戶 | ABP.IO",
"ManageYourAccount": "管理您的帳戶"
}
}

9
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json

@ -358,6 +358,13 @@
"LicensePrice": "License price",
"PurchaseDate": "Purchase date",
"IsAbpBookDownloaded": "ABP book downloaded",
"IsMasteringAbpBookDownloadEnabled": "Abp book download enable"
"IsMasteringAbpBookDownloadEnabled": "ABP Book download enabled",
"Permission:Accounting:CustomPaymentLinkGenerator": "Generate Custom Payment Link",
"CustomPaymentLink": "Custom Payment Link",
"Menu:CustomPaymentLink": "Custom Payment Link",
"Amount": "Amount",
"GenerateCustomPaymentLink": "Generate Custom Payment Link",
"GeneratedPaymentLink": "Generated Payment Link",
"CopyText": "Copy Text"
}
}

349
docs/en/API/API-Versioning.md

@ -0,0 +1,349 @@
# API Versioning System
ABP Framework integrates the [ASPNET-API-Versioning](https://github.com/dotnet/aspnet-api-versioning/wiki) feature and adapts to C# and JavaScript Static Client Proxies and [Auto API Controller](API/Auto-API-Controllers.md).
## Enable API Versioning
```cs
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpApiVersioning(options =>
{
// Show neutral/versionless APIs.
options.UseApiBehavior = false;
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = true;
});
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ChangeControllerModelApiExplorerGroupName = false;
});
}
```
## C# and JavaScript Static Client Proxies
This feature does not compatible with [URL Path Versioning](https://github.com/dotnet/aspnet-api-versioning/wiki/Versioning-via-the-URL-Path), I suggest you always use [Versioning-via-the-Query-String](https://github.com/dotnet/aspnet-api-versioning/wiki/Versioning-via-the-Query-String).
### Example
**Application Services:**
```cs
public interface IBookAppService : IApplicationService
{
Task<BookDto> GetAsync();
}
public interface IBookV2AppService : IApplicationService
{
Task<BookDto> GetAsync();
Task<BookDto> GetAsync(string isbn);
}
```
**HttpApi Controillers:**
```cs
[Area(BookStoreRemoteServiceConsts.ModuleName)]
[RemoteService(Name = BookStoreRemoteServiceConsts.RemoteServiceName)]
[ApiVersion("1.0", Deprecated = true)]
[ApiController]
[ControllerName("Book")]
[Route("api/BookStore/Book")]
public class BookController : BookStoreController, IBookAppService
{
private readonly IBookAppService _bookAppService;
public BookController(IBookAppService bookAppService)
{
_bookAppService = bookAppService;
}
[HttpGet]
public async Task<BookDto> GetAsync()
{
return await _bookAppService.GetAsync();
}
}
[Area(BookStoreRemoteServiceConsts.ModuleName)]
[RemoteService(Name = BookStoreRemoteServiceConsts.RemoteServiceName)]
[ApiVersion("2.0")]
[ApiController]
[ControllerName("Book")]
[Route("api/BookStore/Book")]
public class BookV2Controller : BookStoreController, IBookV2AppService
{
private readonly IBookV2AppService _bookAppService;
public BookV2Controller(IBookV2AppService bookAppService)
{
_bookAppService = bookAppService;
}
[HttpGet]
public async Task<BookDto> GetAsync()
{
return await _bookAppService.GetAsync();
}
[HttpGet]
[Route("{isbn}")]
public async Task<BookDto> GetAsync(string isbn)
{
return await _bookAppService.GetAsync(isbn);
}
}
```
**Generated CS and JS proxies:**
```cs
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IBookAppService), typeof(BookClientProxy))]
public partial class BookClientProxy : ClientProxyBase<IBookAppService>, IBookAppService
{
public virtual async Task<BookDto> GetAsync()
{
return await RequestAsync<BookDto>(nameof(GetAsync));
}
}
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IBookV2AppService), typeof(BookV2ClientProxy))]
public partial class BookV2ClientProxy : ClientProxyBase<IBookV2AppService>, IBookV2AppService
{
public virtual async Task<BookDto> GetAsync()
{
return await RequestAsync<BookDto>(nameof(GetAsync));
}
public virtual async Task<BookDto> GetAsync(string isbn)
{
return await RequestAsync<BookDto>(nameof(GetAsync), new ClientProxyRequestTypeValue
{
{ typeof(string), isbn }
});
}
}
```
```js
// controller bookStore.books.book
(function(){
abp.utils.createNamespace(window, 'bookStore.books.book');
bookStore.books.book.get = function(api_version, ajaxParams) {
var api_version = api_version ? api_version : '1.0';
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/BookStore/Book' + abp.utils.buildQueryString([{ name: 'api-version', value: api_version }]) + '',
type: 'GET'
}, ajaxParams));
};
})();
// controller bookStore.books.bookV2
(function(){
abp.utils.createNamespace(window, 'bookStore.books.bookV2');
bookStore.books.bookV2.get = function(api_version, ajaxParams) {
var api_version = api_version ? api_version : '2.0';
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/BookStore/Book' + abp.utils.buildQueryString([{ name: 'api-version', value: api_version }]) + '',
type: 'GET'
}, ajaxParams));
};
bookStore.books.bookV2.getAsyncByIsbn = function(isbn, api_version, ajaxParams) {
var api_version = api_version ? api_version : '2.0';
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/BookStore/Book/' + isbn + '' + abp.utils.buildQueryString([{ name: 'api-version', value: api_version }]) + '',
type: 'GET'
}, ajaxParams));
};
})();
```
## Manually change version
If an application service class supports multiple versions. You can inject `ICurrentApiVersionInfo` to switch versions in C#.
```cs
var currentApiVersionInfo = _abpApplication.ServiceProvider.GetRequiredService<ICurrentApiVersionInfo>();
var bookV4AppService = _abpApplication.ServiceProvider.GetRequiredService<IBookV4AppService>();
using (currentApiVersionInfo.Change(new ApiVersionInfo(ParameterBindingSources.Query, "4.0")))
{
book = await bookV4AppService.GetAsync();
logger.LogWarning(book.Title);
logger.LogWarning(book.ISBN);
}
using (currentApiVersionInfo.Change(new ApiVersionInfo(ParameterBindingSources.Query, "4.1")))
{
book = await bookV4AppService.GetAsync();
logger.LogWarning(book.Title);
logger.LogWarning(book.ISBN);
}
```
We have made a default version in the JS proxy. Of course, you can also manually change the version.
```js
bookStore.books.bookV4.get("4.0") // Manually change the version.
//Title: Mastering ABP Framework V4.0
bookStore.books.bookV4.get() // The latest supported version is used by default.
//Title: Mastering ABP Framework V4.1
```
## Auto API Controller
```cs
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpAspNetCoreMvcOptions>(options =>
{
//2.0 Version
options.ConventionalControllers.Create(typeof(BookStoreWebAppModule).Assembly, opts =>
{
opts.TypePredicate = t => t.Namespace == typeof(BookStore.Controllers.ConventionalControllers.v2.TodoAppService).Namespace;
opts.ApiVersions.Add(new ApiVersion(2, 0));
});
//1.0 Compatibility version
options.ConventionalControllers.Create(typeof(BookStoreWebAppModule).Assembly, opts =>
{
opts.TypePredicate = t => t.Namespace == typeof(BookStore.Controllers.ConventionalControllers.v1.TodoAppService).Namespace;
opts.ApiVersions.Add(new ApiVersion(1, 0));
});
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
var preActions = context.Services.GetPreConfigureActions<AbpAspNetCoreMvcOptions>();
Configure<AbpAspNetCoreMvcOptions>(options =>
{
preActions.Configure(options);
});
context.Services.AddAbpApiVersioning(options =>
{
// Show neutral/versionless APIs.
options.UseApiBehavior = false;
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = true;
options.ConfigureAbp(preActions.Configure());
});
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ChangeControllerModelApiExplorerGroupName = false;
});
}
```
## Swagger/VersionedApiExplorer
```cs
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpApiVersioning(options =>
{
// Show neutral/versionless APIs.
options.UseApiBehavior = false;
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = true;
});
context.Services.AddVersionedApiExplorer(
options =>
{
// add the versioned api explorer, which also adds IApiVersionDescriptionProvider service
// note: the specified format code will format the version as "'v'major[.minor][-status]"
options.GroupNameFormat = "'v'VVV";
// note: this option is only necessary when versioning by url segment. the SubstitutionFormat
// can also be used to control the format of the API version in route templates
options.SubstituteApiVersionInUrl = true;
});
context.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
context.Services.AddAbpSwaggerGen(
options =>
{
// add a custom operation filter which sets default values
options.OperationFilter<SwaggerDefaultValues>();
options.CustomSchemaIds(type => type.FullName);
});
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ChangeControllerModelApiExplorerGroupName = false;
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseErrorPage();
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAbpRequestLocalization();
app.UseSwagger();
app.UseSwaggerUI(
options =>
{
var provider = app.ApplicationServices.GetRequiredService<IApiVersionDescriptionProvider>();
// 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.UseConfiguredEndpoints();
}
```
## Custom multi-version API controller.
ABP Framework will not affect to your APIs, you can freely implement your APIs according to Microsoft's documentation.
Get more from https://github.com/dotnet/aspnet-api-versioning/wiki
## Sample source code
You can get the complete sample source code in https://github.com/abpframework/abp-samples/tree/master/Api-Versioning

98
docs/en/Community-Articles/2022-02-06-How-to-Hide-ABP-Related-Endpoints-on-Swagger-UI/POST.md

@ -36,7 +36,64 @@ After we've run the application and signed in, we can navigate to **/swagger** t
In our scenario, we will hide endpoints that start with "/api/abp". So let's start to do this.
> In this sample project, we only hide our endpoints that start with the "/api/abp" prefix by definining it in our [CustomSwaggerFilter](https://github.com/EngincanV/ABP-Hide-Swagger-Endpoint-Demo/blob/main/src/SwaggerSettingsDemo.Web/Filters/CustomSwaggerFilter.cs#L29). If you want to hide some other endpoints, you can update the class.
### Creating a Document Filter To Hide ABP Related Endpoints
> In this sample project, we'll only hide our endpoints that start with the "/api/abp" prefix by defining it in our [CustomSwaggerFilter](https://github.com/EngincanV/ABP-Hide-Swagger-Endpoint-Demo/blob/main/src/SwaggerSettingsDemo.Web/Filters/CustomSwaggerFilter.cs#L28) class. If you want to hide some other endpoints, you can update the class.
To hide/show ABP related endpoints on Swagger UI, we can create a [`DocumentFilter`](https://github.com/domaindrivendev/Swashbuckle.AspNetCore#document-filters). By creating a document filter, we can have full control over the endpoints that need to be shown.
* Create a document filter class named `CustomSwaggerFilter`(/Filters/CustomSwaggerFilter.cs) in the `.Web` layer:
> For now, we'll implement this class as hard-coded without checking any settings. Then we'll define a setting for this purpose, and add a new setting management section to manage it on runtime (Enable/Disable).
```csharp
using System.Linq;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace SwaggerSettingsDemo.Web.Filters;
public class CustomSwaggerFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
//remove paths those start with /api/abp prefix
swaggerDoc.Paths
.Where(x => x.Key.ToLowerInvariant().StartsWith("/api/abp"))
.ToList()
.ForEach(x => swaggerDoc.Paths.Remove(x.Key));
}
}
```
We've created a simple class that implements `IDocumentFilter.Apply` method. In that method, we simply remove paths those start with "/api/abp" prefix. You can customize this expression for your needs, I just wanted to hide endpoints those start with "/api/abp" prefix as a sample.
* Then, we need to add this document filter as a swagger document filter. To do this we need to open the `ConfigureSwaggerServices` method in the `*WebModule.cs` class and update the content as below:
```csharp
private void ConfigureSwaggerServices(IServiceCollection services)
{
services.AddAbpSwaggerGen(
options =>
{
//...
//add a new document filter
options.DocumentFilter<CustomSwaggerFilter>();
}
);
}
```
Now we can run the application again and navigate to **/swagger** endpoint to see endpoints.
![](./swagger-document-filter.png)
As you can see in the above image, two endpoints are hidden now (**AbpApiDefinition** and **AbpApplicationConfiguration**).
Let's make it more dynamic and add a setting management section to our **/Settings** page. By doing that, we can dynamically show/hide ABP-related endpoints by checking setting values and applying filtering to Swagger UI on runtime.
### Defining Settings
* Firstly, create a class named `SwaggerSettingConsts` (under `*.Domain.Shared` project):
@ -83,10 +140,12 @@ public class SwaggerSettingsDemoSettingDefinitionProvider : SettingDefinitionPro
}
```
Here we've defined a setting to use in our application.
Here we've defined a setting to use it in our application.
> ABP automatically discovers this class and registers the setting definitions.
### Creating Application Service Methods for Swagger Setting Management
After defining a setting, now we can create an application service interface and add two methods to simply get or update the value of our setting.
* Create an application service interface named `ISwaggerSettingAppService` (or any name you want):
@ -137,12 +196,16 @@ public class SwaggerSettingAppService : SwaggerSettingsDemoAppService, ISwaggerS
}
```
> Here, I didn't make any permission check to keep the article as short as possible. So, if you want to use it on your system, you should define permissions and make permission checks, in these application service methods.
We've injected two interfaces for the application service implementation: `ISettingProvider` and `ISettingManager`
> **ISettingProvider**: Used for getting the value of a setting or getting the values of all settings. It's recommended to use it to read the setting values because it implements caching.
> **ISettingManager**: Used for getting and setting the values of the settings.
### Creating a New Setting Management Section
After implementing our application service, now we can add a new group to our "Setting Management UI".
* Open the `*.Web` project and create a file named `SwaggerHideEndpointsViewComponent`(/Components/SwaggerHideEndpoints/SwaggerHideEndpointsViewComponent.cs):
@ -307,9 +370,13 @@ After all these steps, if we run our application and navigate to **/SettingManag
![](./swagger-settings.png)
To hide/show ABP related endpoints on Swagger UI, we can create a [`DocumentFilter`](https://github.com/domaindrivendev/Swashbuckle.AspNetCore#document-filters). By creating a document filter, we can have full control over the endpoints that need to be shown.
### Update the Document Filter
* Create a document filter class named `CustomSwaggerFilter`(/Filters/CustomSwaggerFilter.cs) in the `.Web` layer:
We've already defined a document filter but it is hard-coded, in other words, it hides ABP-related endpoints in every case without checking the setting value.
So, we need to update the `CustomSwaggerFilter` class and apply filtering only if the setting value is true.
* To do that, update the `CustomSwaggerFilter` method as below:
```csharp
using System.Linq;
@ -346,32 +413,15 @@ public class CustomSwaggerFilter : IDocumentFilter
}
```
We've created a simple class that implements `IDocumentFilter.Apply` method. And in that method, we simply need to get our setting value to see whether should we enable hiding ABP related endpoints or not. To do this, we've used the `ISwaggerSettingAppService.GetSettingByNameAsync` method to get the setting value, but as you can see we've wrapped it with the `AsyncHelper.RunSync` method because `IDocumentFilter.Apply` is not an async method so we need to run this method synchronously.
Here we simply need to get our setting value to see whether should we enable hiding ABP related endpoints or not. We've used the `ISwaggerSettingAppService.GetSettingByNameAsync` method to get the setting value, but as you can see we've wrapped it with the `AsyncHelper.RunSync` method because `IDocumentFilter.Apply` is not an async method so we need to run this method synchronously.
After getting the setting value, we need to ensure that it's both a valid and true setting value, otherwise we don't need to filter the endpoints and show all of them.
If the setting value is true, we can simply remove the paths that start with "/api/abp" prefix.
If the setting value is true, we can simply remove the paths that start with the "/api/abp" prefix as before.
> ABP provides us a class named AsyncHelper and this class provides some helper methods to work with async methods. E.g. RunSync method in here runs the async method synchronously.
* Finally, we can add this document filter as a swagger document filter. To do this we need to open the `ConfigureSwaggerServices` method in the `*WebModule.cs` class and update the content as below:
```csharp
private void ConfigureSwaggerServices(IServiceCollection services)
{
services.AddAbpSwaggerGen(
options =>
{
//...
options.DocumentFilter<CustomSwaggerFilter>(); //add a new document filter
}
);
}
```
That's it. Now we can open the Setting Management page and enable/disable the swagger option and see its effect.
That's it. Now we can open the Setting Management page and enable/disable the swagger option by selecting the checkbox and show/hide ABP-related endpoints on runtime.
![](./swagger-hide-endpoints.gif)

BIN
docs/en/Community-Articles/2022-02-06-How-to-Hide-ABP-Related-Endpoints-on-Swagger-UI/swagger-document-filter.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

4
docs/en/docs-nav.json

@ -590,6 +590,10 @@
{
"text": "Swagger Integration",
"path": "API/Swagger-Integration.md"
},
{
"text": "API Versioning",
"path": "API/API-Versioning.md"
}
]
},

347
docs/zh-Hans/API/API-Versioning.md

@ -0,0 +1,347 @@
# ABP版本控制系统
ABP框架集成了[ASPNET-API-版本控制](https://github.com/dotnet/aspnet-api-versioning/wiki)功能并适配C#和JavaScript静态代理和[自动API控制器](API/Auto-API-Controllers.md).
## 启用API版本控制
```cs
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpApiVersioning(options =>
{
// Show neutral/versionless APIs.
options.UseApiBehavior = false;
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = true;
});
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ChangeControllerModelApiExplorerGroupName = false;
});
}
```
## C# 和 JavaScript 静态客户端代理
这个功能不兼容[URL路径版本控制](https://github.com/dotnet/aspnet-api-versioning/wiki/Versioning-via-the-URL-Path), 建议你始终使用[Query-String版本控制](https://github.com/dotnet/aspnet-api-versioning/wiki/Versioning-via-the-Query-String)
### 示例
**Application Services:**
```cs
public interface IBookAppService : IApplicationService
{
Task<BookDto> GetAsync();
}
public interface IBookV2AppService : IApplicationService
{
Task<BookDto> GetAsync();
Task<BookDto> GetAsync(string isbn);
}
```
**HttpApi Controillers:**
```cs
[Area(BookStoreRemoteServiceConsts.ModuleName)]
[RemoteService(Name = BookStoreRemoteServiceConsts.RemoteServiceName)]
[ApiVersion("1.0", Deprecated = true)]
[ApiController]
[ControllerName("Book")]
[Route("api/BookStore/Book")]
public class BookController : BookStoreController, IBookAppService
{
private readonly IBookAppService _bookAppService;
public BookController(IBookAppService bookAppService)
{
_bookAppService = bookAppService;
}
[HttpGet]
public async Task<BookDto> GetAsync()
{
return await _bookAppService.GetAsync();
}
}
[Area(BookStoreRemoteServiceConsts.ModuleName)]
[RemoteService(Name = BookStoreRemoteServiceConsts.RemoteServiceName)]
[ApiVersion("2.0")]
[ApiController]
[ControllerName("Book")]
[Route("api/BookStore/Book")]
public class BookV2Controller : BookStoreController, IBookV2AppService
{
private readonly IBookV2AppService _bookAppService;
public BookV2Controller(IBookV2AppService bookAppService)
{
_bookAppService = bookAppService;
}
[HttpGet]
public async Task<BookDto> GetAsync()
{
return await _bookAppService.GetAsync();
}
[HttpGet]
[Route("{isbn}")]
public async Task<BookDto> GetAsync(string isbn)
{
return await _bookAppService.GetAsync(isbn);
}
}
```
**生成 CS 和 JS 代理:**
```cs
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IBookAppService), typeof(BookClientProxy))]
public partial class BookClientProxy : ClientProxyBase<IBookAppService>, IBookAppService
{
public virtual async Task<BookDto> GetAsync()
{
return await RequestAsync<BookDto>(nameof(GetAsync));
}
}
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IBookV2AppService), typeof(BookV2ClientProxy))]
public partial class BookV2ClientProxy : ClientProxyBase<IBookV2AppService>, IBookV2AppService
{
public virtual async Task<BookDto> GetAsync()
{
return await RequestAsync<BookDto>(nameof(GetAsync));
}
public virtual async Task<BookDto> GetAsync(string isbn)
{
return await RequestAsync<BookDto>(nameof(GetAsync), new ClientProxyRequestTypeValue
{
{ typeof(string), isbn }
});
}
}
```
```js
// controller bookStore.books.book
(function(){
abp.utils.createNamespace(window, 'bookStore.books.book');
bookStore.books.book.get = function(api_version, ajaxParams) {
var api_version = api_version ? api_version : '1.0';
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/BookStore/Book' + abp.utils.buildQueryString([{ name: 'api-version', value: api_version }]) + '',
type: 'GET'
}, ajaxParams));
};
})();
// controller bookStore.books.bookV2
(function(){
abp.utils.createNamespace(window, 'bookStore.books.bookV2');
bookStore.books.bookV2.get = function(api_version, ajaxParams) {
var api_version = api_version ? api_version : '2.0';
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/BookStore/Book' + abp.utils.buildQueryString([{ name: 'api-version', value: api_version }]) + '',
type: 'GET'
}, ajaxParams));
};
bookStore.books.bookV2.getAsyncByIsbn = function(isbn, api_version, ajaxParams) {
var api_version = api_version ? api_version : '2.0';
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/BookStore/Book/' + isbn + '' + abp.utils.buildQueryString([{ name: 'api-version', value: api_version }]) + '',
type: 'GET'
}, ajaxParams));
};
})();
```
## 手动更改版本
如果应用服务支持多版本, 你可以注入 `ICurrentApiVersionInfo` 来切换版本.
```cs
var currentApiVersionInfo = _abpApplication.ServiceProvider.GetRequiredService<ICurrentApiVersionInfo>();
var bookV4AppService = _abpApplication.ServiceProvider.GetRequiredService<IBookV4AppService>();
using (currentApiVersionInfo.Change(new ApiVersionInfo(ParameterBindingSources.Query, "4.0")))
{
book = await bookV4AppService.GetAsync();
logger.LogWarning(book.Title);
logger.LogWarning(book.ISBN);
}
using (currentApiVersionInfo.Change(new ApiVersionInfo(ParameterBindingSources.Query, "4.1")))
{
book = await bookV4AppService.GetAsync();
logger.LogWarning(book.Title);
logger.LogWarning(book.ISBN);
}
```
在JS代理中有一个默认版本, 当然你也可以手动更改.
```js
bookStore.books.bookV4.get("4.0") // Manually change the version.
//Title: Mastering ABP Framework V4.0
bookStore.books.bookV4.get() // The latest supported version is used by default.
//Title: Mastering ABP Framework V4.1
```
## 自动API控制器
```cs
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpAspNetCoreMvcOptions>(options =>
{
//2.0 Version
options.ConventionalControllers.Create(typeof(BookStoreWebAppModule).Assembly, opts =>
{
opts.TypePredicate = t => t.Namespace == typeof(BookStore.Controllers.ConventionalControllers.v2.TodoAppService).Namespace;
opts.ApiVersions.Add(new ApiVersion(2, 0));
});
//1.0 Compatibility version
options.ConventionalControllers.Create(typeof(BookStoreWebAppModule).Assembly, opts =>
{
opts.TypePredicate = t => t.Namespace == typeof(BookStore.Controllers.ConventionalControllers.v1.TodoAppService).Namespace;
opts.ApiVersions.Add(new ApiVersion(1, 0));
});
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
var preActions = context.Services.GetPreConfigureActions<AbpAspNetCoreMvcOptions>();
Configure<AbpAspNetCoreMvcOptions>(options =>
{
preActions.Configure(options);
});
context.Services.AddAbpApiVersioning(options =>
{
// Show neutral/versionless APIs.
options.UseApiBehavior = false;
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = true;
options.ConfigureAbp(preActions.Configure());
});
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ChangeControllerModelApiExplorerGroupName = false;
});
}
```
## Swagger/VersionedApiExplorer
```cs
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpApiVersioning(options =>
{
// Show neutral/versionless APIs.
options.UseApiBehavior = false;
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = true;
});
context.Services.AddVersionedApiExplorer(
options =>
{
// add the versioned api explorer, which also adds IApiVersionDescriptionProvider service
// note: the specified format code will format the version as "'v'major[.minor][-status]"
options.GroupNameFormat = "'v'VVV";
// note: this option is only necessary when versioning by url segment. the SubstitutionFormat
// can also be used to control the format of the API version in route templates
options.SubstituteApiVersionInUrl = true;
});
context.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
context.Services.AddAbpSwaggerGen(
options =>
{
// add a custom operation filter which sets default values
options.OperationFilter<SwaggerDefaultValues>();
options.CustomSchemaIds(type => type.FullName);
});
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ChangeControllerModelApiExplorerGroupName = false;
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseErrorPage();
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAbpRequestLocalization();
app.UseSwagger();
app.UseSwaggerUI(
options =>
{
var provider = app.ApplicationServices.GetRequiredService<IApiVersionDescriptionProvider>();
// 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.UseConfiguredEndpoints();
}
```
## 自定义多版本API控制器
ABP框架不会影响你的API, 你可以根据微软文档自由的实现你的API.
参阅: https://github.com/dotnet/aspnet-api-versioning/wiki
## 示例源码
你可以在这里得到完整的示例源码: https://github.com/abpframework/abp-samples/tree/master/Api-Versioning

4
docs/zh-Hans/docs-nav.json

@ -444,6 +444,10 @@
"path": "API/Application-Configuration.md"
}
]
},
{
"text": "API版本控制",
"path": "API/API-Versioning.md"
}
]
},

3
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcOptions.cs

@ -16,11 +16,14 @@ public class AbpAspNetCoreMvcOptions
public bool EnableRazorRuntimeCompilationOnDevelopment { get; set; }
public bool ChangeControllerModelApiExplorerGroupName { get; set; }
public AbpAspNetCoreMvcOptions()
{
ConventionalControllers = new AbpConventionalControllerOptions();
IgnoredControllersOnModelExclusion = new HashSet<Type>();
AutoModelValidation = true;
EnableRazorRuntimeCompilationOnDevelopment = true;
ChangeControllerModelApiExplorerGroupName = true;
}
}

34
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiDescriptionExtensions.cs

@ -0,0 +1,34 @@
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Controllers;
using Volo.Abp.Reflection;
namespace Volo.Abp.AspNetCore.Mvc;
public static class ApiDescriptionExtensions
{
public static bool IsRemoteService(this ApiDescription actionDescriptor)
{
if (actionDescriptor.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
{
var remoteServiceAttr = ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<RemoteServiceAttribute>(controllerActionDescriptor.MethodInfo);
if (remoteServiceAttr != null && remoteServiceAttr.IsEnabled)
{
return true;
}
remoteServiceAttr = ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<RemoteServiceAttribute>(controllerActionDescriptor.ControllerTypeInfo);
if (remoteServiceAttr != null && remoteServiceAttr.IsEnabled)
{
return true;
}
if (typeof(IRemoteService).IsAssignableFrom(controllerActionDescriptor.ControllerTypeInfo))
{
return true;
}
}
return false;
}
}

27
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs

@ -42,7 +42,7 @@ public class AbpRemoteServiceApiDescriptionProvider : IApiDescriptionProvider, I
{
foreach (var apiResponseType in GetApiResponseTypes())
{
foreach (var result in context.Results.Where(IsRemoteService))
foreach (var result in context.Results.Where(x => x.IsRemoteService()))
{
var actionProducesResponseTypeAttributes =
ReflectionHelper.GetAttributesOfMemberOrDeclaringType<ProducesResponseTypeAttribute>(
@ -85,29 +85,4 @@ public class AbpRemoteServiceApiDescriptionProvider : IApiDescriptionProvider, I
return _options.SupportedResponseTypes;
}
protected virtual bool IsRemoteService(ApiDescription actionDescriptor)
{
if (actionDescriptor.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
{
var remoteServiceAttr = ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<RemoteServiceAttribute>(controllerActionDescriptor.MethodInfo);
if (remoteServiceAttr != null && remoteServiceAttr.IsEnabled)
{
return true;
}
remoteServiceAttr = ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault<RemoteServiceAttribute>(controllerActionDescriptor.ControllerTypeInfo);
if (remoteServiceAttr != null && remoteServiceAttr.IsEnabled)
{
return true;
}
if (typeof(IRemoteService).IsAssignableFrom(controllerActionDescriptor.ControllerTypeInfo))
{
return true;
}
}
return false;
}
}

17
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs

@ -8,7 +8,9 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
@ -64,6 +66,19 @@ public class AspNetCoreApiDescriptionModelProvider : IApiDescriptionModelProvide
}
}
foreach (var (_, module) in model.Modules)
{
var controllers = module.Controllers.GroupBy(x => x.Value.Type).ToList();
foreach (var controller in controllers.Where(x => x.Count() > 1))
{
var removedController = module.Controllers.RemoveAll(x => x.Value.IsRemoteService && controller.OrderBy(c => c.Value.ControllerGroupName).Skip(1).Contains(x));
foreach (var removed in removedController)
{
Logger.LogInformation($"The controller named '{removed.Value.Type}' was removed from ApplicationApiDescriptionModel because it same with other controller.");
}
}
}
return model;
}
@ -87,6 +102,8 @@ public class AspNetCoreApiDescriptionModelProvider : IApiDescriptionModelProvide
var controllerModel = moduleModel.GetOrAddController(
_options.ControllerNameGenerator(controllerType, setting),
FindGroupName(controllerType) ?? apiDescription.GroupName,
apiDescription.IsRemoteService(),
apiDescription.GetProperty<ApiVersion>()?.ToString(),
controllerType,
_modelOptions.IgnoredInterfaces
);

2
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs

@ -207,7 +207,7 @@ public class AbpServiceConvention : IAbpServiceConvention, ITransientDependency
protected virtual void ConfigureApiExplorer(ControllerModel controller)
{
if (controller.ApiExplorer.GroupName.IsNullOrEmpty())
if (Options.ChangeControllerModelApiExplorerGroupName && controller.ApiExplorer.GroupName.IsNullOrEmpty())
{
controller.ApiExplorer.GroupName = controller.ControllerName;
}

2
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Versioning/HttpContextRequestedApiVersion.cs

@ -6,7 +6,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning;
public class HttpContextRequestedApiVersion : IRequestedApiVersion
{
public string Current => _httpContextAccessor.HttpContext?.GetRequestedApiVersion().ToString();
public string Current => _httpContextAccessor.HttpContext?.GetRequestedApiVersion()?.ToString();
private readonly IHttpContextAccessor _httpContextAccessor;

2
framework/src/Volo.Abp.Http.Client.Web/Volo/Abp/Http/Client/Web/Conventions/AbpHttpClientProxyServiceConvention.cs

@ -170,7 +170,7 @@ public class AbpHttpClientProxyServiceConvention : AbpServiceConvention
{
if (ControllerWithAttributeRoute.Contains(controller))
{
if (controller.ApiExplorer.GroupName.IsNullOrEmpty())
if (Options.ChangeControllerModelApiExplorerGroupName && controller.ApiExplorer.GroupName.IsNullOrEmpty())
{
controller.ApiExplorer.GroupName = controller.ControllerName;
}

10
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ClientProxyBase.cs

@ -38,6 +38,7 @@ public class ClientProxyBase<TService> : ITransientDependency
protected IRemoteServiceHttpClientAuthenticator ClientAuthenticator => LazyServiceProvider.LazyGetRequiredService<IRemoteServiceHttpClientAuthenticator>();
protected ClientProxyRequestPayloadBuilder ClientProxyRequestPayloadBuilder => LazyServiceProvider.LazyGetRequiredService<ClientProxyRequestPayloadBuilder>();
protected ClientProxyUrlBuilder ClientProxyUrlBuilder => LazyServiceProvider.LazyGetRequiredService<ClientProxyUrlBuilder>();
protected ICurrentApiVersionInfo CurrentApiVersionInfo => LazyServiceProvider.LazyGetRequiredService<ICurrentApiVersionInfo>();
protected virtual async Task RequestAsync(string methodName, ClientProxyRequestTypeValue arguments = null)
{
@ -62,10 +63,14 @@ public class ClientProxyBase<TService> : ITransientDependency
{
throw new AbpException($"The API description of the {typeof(TService).FullName}.{methodName} method was not found!");
}
return new ClientProxyRequestContext(
action,
action.Parameters
.GroupBy(x => x.NameOnMethod)
//TODO: make names configurable
.Where(x => action.SupportedVersions.Any() && x.Key != "api-version" && x.Key !="apiVersion")
.Select((x, i) => new KeyValuePair<string, object>(x.Key, arguments.Values[i].Value))
.ToDictionary(x => x.Key, x => x.Value),
typeof(TService));
@ -156,6 +161,11 @@ public class ClientProxyBase<TService> : ITransientDependency
protected virtual async Task<ApiVersionInfo> GetApiVersionInfoAsync(ClientProxyRequestContext requestContext)
{
if (CurrentApiVersionInfo.ApiVersionInfo != null)
{
return CurrentApiVersionInfo.ApiVersionInfo;
}
var apiVersion = await FindBestApiVersionAsync(requestContext);
//TODO: Make names configurable?

22
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/CurrentApiVersionInfo.cs

@ -0,0 +1,22 @@
using System;
using System.Threading;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Http.Client.ClientProxying;
public class CurrentApiVersionInfo : ICurrentApiVersionInfo, ISingletonDependency
{
public ApiVersionInfo ApiVersionInfo => _currentApiVersionInfo.Value;
private readonly AsyncLocal<ApiVersionInfo> _currentApiVersionInfo = new AsyncLocal<ApiVersionInfo>();
public virtual IDisposable Change(ApiVersionInfo apiVersionInfo)
{
var parent = ApiVersionInfo;
_currentApiVersionInfo.Value = apiVersionInfo;
return new DisposeAction(() =>
{
_currentApiVersionInfo.Value = parent;
});
}
}

10
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/ClientProxying/ICurrentApiVersionInfo.cs

@ -0,0 +1,10 @@
using System;
namespace Volo.Abp.Http.Client.ClientProxying;
public interface ICurrentApiVersionInfo
{
ApiVersionInfo ApiVersionInfo { get; }
IDisposable Change(ApiVersionInfo apiVersionInfo);
}

8
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs

@ -12,6 +12,10 @@ public class ControllerApiDescriptionModel
public string ControllerGroupName { get; set; }
public bool IsRemoteService { get; set; }
public string ApiVersion { get; set; }
public string Type { get; set; }
public List<ControllerInterfaceApiDescriptionModel> Interfaces { get; set; }
@ -23,12 +27,14 @@ public class ControllerApiDescriptionModel
}
public static ControllerApiDescriptionModel Create(string controllerName, string groupName, Type type, [CanBeNull] HashSet<Type> ignoredInterfaces = null)
public static ControllerApiDescriptionModel Create(string controllerName, string groupName, bool isRemoteService, string apiVersion, Type type, [CanBeNull] HashSet<Type> ignoredInterfaces = null)
{
return new ControllerApiDescriptionModel
{
ControllerName = controllerName,
ControllerGroupName = groupName,
IsRemoteService = isRemoteService,
ApiVersion = apiVersion,
Type = type.FullName,
Actions = new Dictionary<string, ActionApiDescriptionModel>(),
Interfaces = type

5
framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ModuleApiDescriptionModel.cs

@ -49,9 +49,10 @@ public class ModuleApiDescriptionModel
return Controllers[controller.Type] = controller;
}
public ControllerApiDescriptionModel GetOrAddController(string name, string groupName, Type type, [CanBeNull] HashSet<Type> ignoredInterfaces = null)
public ControllerApiDescriptionModel GetOrAddController(string name, string groupName, bool isRemoteService, string apiVersion, Type type, [CanBeNull] HashSet<Type> ignoredInterfaces = null)
{
return Controllers.GetOrAdd(type.FullName, () => ControllerApiDescriptionModel.Create(name, groupName, type, ignoredInterfaces));
var key = apiVersion.IsNullOrWhiteSpace() ? type.FullName : $"{apiVersion + "."}{type.FullName}";
return Controllers.GetOrAdd(key, () => ControllerApiDescriptionModel.Create(name, groupName, isRemoteService, apiVersion, type, ignoredInterfaces));
}
public ModuleApiDescriptionModel CreateSubModel(string[] controllers, string[] actions)

2
framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs

@ -114,7 +114,7 @@ public class JQueryProxyScriptGenerator : IProxyScriptGenerator, ITransientDepen
if (versionParam != null)
{
var version = FindBestApiVersion(action);
script.AppendLine($" var {ProxyScriptingJsFuncHelper.NormalizeJsVariableName(versionParam.Name)} = '{version}';");
script.AppendLine($" var {ProxyScriptingJsFuncHelper.NormalizeJsVariableName(versionParam.Name)} = api_version ? api_version : '{version}';");
}
script.AppendLine(" return abp.ajax($.extend(true, {");

2
npm/packs/core/src/abp.js

@ -73,7 +73,7 @@ var abp = abp || {};
abp.localization = abp.localization || {};
abp.localization.values = {};
abp.localization.values = abp.localization.values || {};
abp.localization.localize = function (key, sourceName) {
if (sourceName === '_') { //A convention to suppress the localization

Loading…
Cancel
Save