279 lines
10 KiB
C#
279 lines
10 KiB
C#
using Microsoft.AspNetCore.Mvc.Controllers;
|
|
using Microsoft.OpenApi.Models;
|
|
using Pluralize.NET;
|
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
|
using Swashbuckle.AspNetCore.SwaggerUI;
|
|
|
|
namespace NetinaShop.Api.WebFramework.Swagger;
|
|
|
|
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.EnableAnnotations();
|
|
options.DescribeAllParametersInCamelCase();
|
|
options.IgnoreObsoleteActions();
|
|
|
|
//#region Versioning
|
|
|
|
//// Remove version parameter from all Operations
|
|
//options.OperationFilter<RemoveVersionParameters>();
|
|
|
|
////set version "api/v{version}/[controller]" from current swagger doc verion
|
|
//options.DocumentFilter<SetVersionInPaths>();
|
|
|
|
////Seperate and categorize end-points by doc version
|
|
//options.DocInclusionPredicate((version, desc) =>
|
|
//{
|
|
// if (!desc.TryGetMethodInfo(out var methodInfo)) return false;
|
|
// var versions = methodInfo.DeclaringType
|
|
// .GetCustomAttributes(true)
|
|
// .OfType<ApiVersionAttribute>()
|
|
// .SelectMany(attr => attr.Versions)
|
|
// .ToList();
|
|
|
|
// return versions.Any(v => $"v{v.ToString()}" == version);
|
|
//});
|
|
|
|
//#endregion
|
|
|
|
#region Security
|
|
|
|
var url = $"{baseUrl}/api/auth/login/swagger";
|
|
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
|
{
|
|
Type = SecuritySchemeType.OAuth2,
|
|
Scheme = "Bearer",
|
|
Name = "Bearer",
|
|
Flows = new OpenApiOAuthFlows
|
|
{
|
|
Password = new OpenApiOAuthFlow
|
|
{
|
|
TokenUrl = new Uri(url)
|
|
}
|
|
}
|
|
});
|
|
options.OperationFilter<UnauthorizedResponsesOperationFilter>(true, "Bearer");
|
|
|
|
#endregion
|
|
|
|
#region Customize
|
|
|
|
options.OperationFilter<ApplySummariesOperationFilter>();
|
|
|
|
#endregion
|
|
});
|
|
}
|
|
|
|
public static void UseCustomSwagger(this IApplicationBuilder app,string baseUrl)
|
|
{
|
|
app.UseSwagger(options =>
|
|
{
|
|
options.SerializeAsV2 = true;
|
|
});
|
|
|
|
app.UseSwaggerUI(options =>
|
|
{
|
|
options.InjectStylesheet("/assets/swagger-ui/x3/theme-flattop.css");
|
|
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");
|
|
});
|
|
}
|
|
}
|
|
|
|
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(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].OrderDescription.HasValue())
|
|
// operation.Parameters[0].OrderDescription = $"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
|
|
}
|
|
} |