diff --git a/README.md b/README.md index 427a35b..8ddcd0b 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,18 @@ c.CustomProvider(defaultProvider => new ODataSwaggerProvider(defaultProvider, c, })); ``` +### Enable caching of swagger requests ### + +To enable the built-in cache functionality you must set this configuration: + +```csharp +c.CustomProvider(defaultProvider => new ODataSwaggerProvider(defaultProvider, c, GlobalConfiguration.Configuration).Configure(odataConfig => + { + // Enable Cache for swagger doc requests + odataConfig.EnableSwaggerRequestCaching(); + })); +``` + ### Custom Swagger Routes ### The following snippet demonstrates how to configure a custom swagger route such that it will appear in the Swagger UI: diff --git a/Swashbuckle.OData.Sample/App_Start/SwaggerConfig.cs b/Swashbuckle.OData.Sample/App_Start/SwaggerConfig.cs index 6531b04..34b5298 100644 --- a/Swashbuckle.OData.Sample/App_Start/SwaggerConfig.cs +++ b/Swashbuckle.OData.Sample/App_Start/SwaggerConfig.cs @@ -177,6 +177,9 @@ public static void Register() // Set this flag to include navigation properties in your entity swagger models // odataConfig.IncludeNavigationProperties(); + + // Enable Caché for swagger doc requests + odataConfig.EnableSwaggerRequestCaching(); })); }) .EnableSwaggerUi(c => diff --git a/Swashbuckle.OData.Tests/Fixtures/ODataSwaggerProviderTests.cs b/Swashbuckle.OData.Tests/Fixtures/ODataSwaggerProviderTests.cs index cee71c3..fbc4f5a 100644 --- a/Swashbuckle.OData.Tests/Fixtures/ODataSwaggerProviderTests.cs +++ b/Swashbuckle.OData.Tests/Fixtures/ODataSwaggerProviderTests.cs @@ -145,9 +145,36 @@ public async Task It_supports_both_webapi_and_odata_controllers() } } - private static void Configuration(IAppBuilder appBuilder, Type targetController = null, Action swaggerDocsConfig = null) + /// + /// Test to check if the cached swagger documents have the same value as the non-cached and to check if they are valid + /// + /// + [Test] + public async Task It_supports_caching_swagger_document() + { + Action config = c => c.EnableSwaggerRequestCaching(); + using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(CustomersController), + odataSwaggerDocsConfig: config))) + { + // Arrange + var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress); + + // First request (Non-cached) + var swaggerDocument = await httpClient.GetJsonAsync("swagger/docs/v1"); + + //Cached request + var swaggerDocument2 = await httpClient.GetJsonAsync("swagger/docs/v1"); + + swaggerDocument.ShouldBeEquivalentTo(swaggerDocument2); + + await ValidationUtils.ValidateSwaggerJson(); + } + } + + + private static void Configuration(IAppBuilder appBuilder, Type targetController = null, Action swaggerDocsConfig = null, Action odataSwaggerDocsConfig = null) { - var config = appBuilder.GetStandardHttpConfig(swaggerDocsConfig, null, targetController); + var config = appBuilder.GetStandardHttpConfig(swaggerDocsConfig, odataSwaggerDocsConfig, targetController); var controllerSelector = new UnitTestODataVersionControllerSelector(config, targetController); config.Services.Replace(typeof(IHttpControllerSelector), controllerSelector); diff --git a/Swashbuckle.OData/ODataSwaggerDocsConfig.cs b/Swashbuckle.OData/ODataSwaggerDocsConfig.cs index ad64d57..99eb08f 100644 --- a/Swashbuckle.OData/ODataSwaggerDocsConfig.cs +++ b/Swashbuckle.OData/ODataSwaggerDocsConfig.cs @@ -16,6 +16,7 @@ public class ODataSwaggerDocsConfig private readonly SwaggerDocsConfig _swaggerDocsConfig; private readonly List> _documentFilters; private bool _includeNavigationProperties; + internal bool enableCache; internal ODataSwaggerDocsConfig(SwaggerDocsConfig swaggerDocsConfig, HttpConfiguration httpConfiguration) { @@ -24,8 +25,9 @@ internal ODataSwaggerDocsConfig(SwaggerDocsConfig swaggerDocsConfig, HttpConfigu Configuration = httpConfiguration; _swaggerDocsConfig = swaggerDocsConfig; - _includeNavigationProperties = false; + _includeNavigationProperties = false; _documentFilters = new List>(); + enableCache = false; } internal void DocumentFilter() where TFilter : IDocumentFilter, new() @@ -172,5 +174,10 @@ public void IncludeNavigationProperties() { _includeNavigationProperties = true; } + + public void EnableSwaggerRequestCaching() + { + enableCache = true; + } } } \ No newline at end of file diff --git a/Swashbuckle.OData/ODataSwaggerProvider.cs b/Swashbuckle.OData/ODataSwaggerProvider.cs index a7dccae..0d937d3 100644 --- a/Swashbuckle.OData/ODataSwaggerProvider.cs +++ b/Swashbuckle.OData/ODataSwaggerProvider.cs @@ -9,6 +9,7 @@ using Swashbuckle.Application; using Swashbuckle.OData.Descriptions; using Swashbuckle.Swagger; +using System.Collections.Concurrent; namespace Swashbuckle.OData { @@ -17,6 +18,9 @@ public class ODataSwaggerProvider : ISwaggerProvider private readonly ISwaggerProvider _defaultProvider; private readonly ODataSwaggerDocsConfig _config; + private static ConcurrentDictionary> _cache = + new ConcurrentDictionary>(); + /// /// Initializes a new instance of the class. /// Use this constructor for self-hosted scenarios. @@ -35,6 +39,24 @@ public ODataSwaggerProvider(ISwaggerProvider defaultProvider, SwaggerDocsConfig } public SwaggerDocument GetSwagger(string rootUrl, string apiVersion) + { + if(_config.enableCache) + { + var cacheKey = string.Format("{0}_{1}", rootUrl, apiVersion); + var SwaggerDoc = _cache.GetOrAdd(cacheKey, (key) => + //making GetOrAdd operation thread-safe + new Lazy(() => { + //Getting swagger + return GenerateSwagger(rootUrl, apiVersion); + }) + ); + return SwaggerDoc.Value; + } + else + return GenerateSwagger(rootUrl, apiVersion); + } + + private SwaggerDocument GenerateSwagger(string rootUrl, string apiVersion) { var swashbuckleOptions = _config.GetSwashbuckleOptions();