using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using iPackage.Extensions; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Models; using Pluralize.NET; using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerUI; namespace iPackage.Core.Web.WebFramework.Configurations { public static class SwaggerConfiguration { public static void AddCustomSwagger(this IServiceCollection services, string baseUrl) { services.AddSwaggerGen(options => { //var xmlDuc = Path.Combine(AppContext.BaseDirectory, "swaggerApi.xml"); //options.IncludeXmlComments(xmlDuc,true); options.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", Title = "iGarson Api Dacument", Description = "iGarson api for clients that wana use", License = new OpenApiLicense { Name = "Vira Safir Fanavar " }, Contact = new OpenApiContact { Name = "Amir Hossein Khademi", Email = "avvampier@gmail.com", Url = new Uri("http://amir-khademi.ir/") } }); options.SwaggerDoc("v2", new OpenApiInfo { Version = "v2", Title = "iGarson Api Dacument", Description = "iGarson api for clients that wana use", License = new OpenApiLicense { Name = "Vira Safir Fanavar " }, Contact = new OpenApiContact { Name = "Amir Hossein Khademi", Email = "avvampier@gmail.com", Url = new Uri("http://amir-khademi.ir/") } }); //options.EnableAnnotations(); options.DescribeAllParametersInCamelCase(); options.IgnoreObsoleteActions(); #region Versioning // Remove version parameter from all Operations options.OperationFilter(); //set version "api/v{version}/[controller]" from current swagger doc verion options.DocumentFilter(); //Seperate and categorize end-points by doc version options.DocInclusionPredicate((version, desc) => { if (!desc.TryGetMethodInfo(out MethodInfo methodInfo)) return false; var versions = methodInfo.DeclaringType .GetCustomAttributes(true) .OfType() .SelectMany(attr => attr.Versions) .ToList(); return versions.Any(v => $"v{v.ToString()}" == version); }); #endregion #region Security string url = $"{baseUrl}/api/v1/user/LoginSwagger"; options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Type = SecuritySchemeType.OAuth2, Scheme = "Bearer", Name = "Bearer", Flows = new OpenApiOAuthFlows { Password = new OpenApiOAuthFlow { TokenUrl = new Uri(url), } } }); options.OperationFilter(true, "Bearer"); #endregion #region Customize options.OperationFilter(); #endregion }); } public static void UseCustomSwaager(this IApplicationBuilder app, string baseUrl) { app.UseSwagger(); app.UseSwaggerUI(options => { options.DocExpansion(DocExpansion.None); // Display options.DefaultModelExpandDepth(2); options.DefaultModelRendering(ModelRendering.Model); options.DefaultModelsExpandDepth(-1); options.DisplayOperationId(); options.DisplayRequestDuration(); options.EnableDeepLinking(); options.EnableFilter(); options.ShowExtensions(); options.OAuthUseBasicAuthenticationWithAccessCodeGrant(); options.SwaggerEndpoint($"{baseUrl}/swagger/v1/swagger.json", "V1 Docs"); options.SwaggerEndpoint($"{baseUrl}/swagger/v2/swagger.json", "V2 Docs"); }); } } public class RemoveVersionParameters : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { // Remove version parameter from all Operations var versionParameter = operation.Parameters.SingleOrDefault(p => p.Name == "version"); if (versionParameter != null) operation.Parameters.Remove(versionParameter); } } public class SetVersionInPaths : IDocumentFilter { public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { if (swaggerDoc == null) throw new ArgumentNullException(nameof(swaggerDoc)); var replacements = new OpenApiPaths(); foreach (var (key, value) in swaggerDoc.Paths) { replacements.Add(key.Replace("v{version}", swaggerDoc.Info.Version, StringComparison.InvariantCulture), value); } swaggerDoc.Paths = replacements; } } public class UnauthorizedResponsesOperationFilter : IOperationFilter { private readonly bool includeUnauthorizedAndForbiddenResponses; private readonly string schemeName; public UnauthorizedResponsesOperationFilter(bool includeUnauthorizedAndForbiddenResponses, string schemeName = "Bearer") { this.includeUnauthorizedAndForbiddenResponses = includeUnauthorizedAndForbiddenResponses; this.schemeName = schemeName; } public void Apply(OpenApiOperation operation, OperationFilterContext context) { var filters = context.ApiDescription.ActionDescriptor.FilterDescriptors; var hasAnynomousEndPoint = context.ApiDescription.ActionDescriptor.EndpointMetadata.Any(e => e.GetType() == typeof(Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute)); //var hasAnonymous = filters.Any(p => p.Filter is AllowAnonymousFilter); if (hasAnynomousEndPoint) return; /*var hasAuthorize = filters.Any(p => p.Filter is AuthorizeFilter); if (!hasAuthorize) return;*/ if (includeUnauthorizedAndForbiddenResponses) { operation.Responses.TryAdd("401", new OpenApiResponse { Description = "Unauthorized" }); operation.Responses.TryAdd("403", new OpenApiResponse { Description = "Forbidden" }); } operation.Security.Add(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, new string[] {} } }); } } public class ApplySummariesOperationFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { var controllerActionDescriptor = context.ApiDescription.ActionDescriptor as ControllerActionDescriptor; if (controllerActionDescriptor == null) return; var pluralizer = new Pluralizer(); var actionName = controllerActionDescriptor.ActionName; var singularizeName = pluralizer.Singularize(controllerActionDescriptor.ControllerName); var pluralizeName = pluralizer.Pluralize(singularizeName); var parameterCount = operation.Parameters.Where(p => p.Name != "version" && p.Name != "api-version").Count(); if (IsGetAllAction()) { if (!operation.Summary.HasValue()) operation.Summary = $"Returns all {pluralizeName}"; } else if (IsActionName("Post", "Create")) { if (!operation.Summary.HasValue()) operation.Summary = $"Creates a {singularizeName}"; if (operation.Parameters.Count > 0 && !operation.Parameters[0].Description.HasValue()) operation.Parameters[0].Description = $"A {singularizeName} representation"; } else if (IsActionName("Read", "Get")) { if (!operation.Summary.HasValue()) operation.Summary = $"Retrieves a {singularizeName} by unique id"; if (operation.Parameters.Count > 0 && !operation.Parameters[0].Description.HasValue()) operation.Parameters[0].Description = $"a unique id for the {singularizeName}"; } else if (IsActionName("Put", "Edit", "Update")) { if (!operation.Summary.HasValue()) operation.Summary = $"Updates a {singularizeName} by unique id"; //if (!operation.Parameters[0].Description.HasValue()) // operation.Parameters[0].Description = $"A unique id for the {singularizeName}"; if (operation.Parameters.Count > 0 && !operation.Parameters[0].Description.HasValue()) operation.Parameters[0].Description = $"A {singularizeName} representation"; } else if (IsActionName("Delete", "Remove")) { if (!operation.Summary.HasValue()) operation.Summary = $"Deletes a {singularizeName} by unique id"; if (operation.Parameters.Count > 0 && !operation.Parameters[0].Description.HasValue()) operation.Parameters[0].Description = $"A unique id for the {singularizeName}"; } else { if (!operation.Summary.HasValue()) operation.Summary = $"{actionName} {pluralizeName}"; } #region Local Functions bool IsGetAllAction() { foreach (var name in new[] { "Get", "Read", "Select" }) { if ((actionName.Equals(name, StringComparison.OrdinalIgnoreCase) && parameterCount == 0) || actionName.Equals($"{name}All", StringComparison.OrdinalIgnoreCase) || actionName.Equals($"{name}{pluralizeName}", StringComparison.OrdinalIgnoreCase) || actionName.Equals($"{name}All{singularizeName}", StringComparison.OrdinalIgnoreCase) || actionName.Equals($"{name}All{pluralizeName}", StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } bool IsActionName(params string[] names) { foreach (var name in names) { if (actionName.Contains(name, StringComparison.OrdinalIgnoreCase) || actionName.Contains($"{name}ById", StringComparison.OrdinalIgnoreCase) || actionName.Contains($"{name}{singularizeName}", StringComparison.OrdinalIgnoreCase) || actionName.Contains($"{name}{singularizeName}ById", StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } #endregion } } }