add support for including all assemblies xml docs in swagger ...

This commit is contained in:
2026-09-03 10:34:28 +03:30
parent c87f80c587
commit 2f02fc793b
2 changed files with 173 additions and 88 deletions
+120 -35
View File
@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -12,8 +13,10 @@ using xCommons.Configurations;
using xCommons.Constants; using xCommons.Constants;
using xCommons.Filters; using xCommons.Filters;
namespace xCommons.Extensions { namespace xCommons.Extensions
public static partial class DIExtensions { {
public static partial class DIExtensions
{
/// <summary> /// <summary>
/// Inject Specific Registered Service from IServiceCollection /// Inject Specific Registered Service from IServiceCollection
@@ -21,7 +24,8 @@ namespace xCommons.Extensions {
/// <param name="source"></param> /// <param name="source"></param>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <returns></returns> /// <returns></returns>
public static T GetRegisteredService<T> (this IServiceCollection source) { public static T GetRegisteredService<T>(this IServiceCollection source)
{
// //
var serviceProvider = source.BuildServiceProvider(); var serviceProvider = source.BuildServiceProvider();
return serviceProvider.GetService<T>(); return serviceProvider.GetService<T>();
@@ -32,12 +36,57 @@ namespace xCommons.Extensions {
/// </summary> /// </summary>
/// <param name="services"></param> /// <param name="services"></param>
/// <param name="configuration"></param> /// <param name="configuration"></param>
public static void AddXAppConfiguration (this IServiceCollection services, IConfiguration configuration) { public static void AddXAppConfiguration(this IServiceCollection services, IConfiguration configuration)
{
// //
var appConfiguration = configuration.GetXAppConfiguration(); var appConfiguration = configuration.GetXAppConfiguration();
services.AddSingleton<XAppConfiguration>(appConfiguration); services.AddSingleton<XAppConfiguration>(appConfiguration);
} }
/// <summary>
/// Search Assemblies, Extract XML Comments and
/// Integrate them inside swagger gen documents ...
/// </summary>
/// <param name="source"></param>
public static void IncludeAssembliesXMLComments(this SwaggerGenOptions source)
{
//
// Extract all Exists .xml files ...
var basePath = AppContext.BaseDirectory;
var xmlFiles = Directory.GetFiles(basePath, "*.xml", SearchOption.TopDirectoryOnly);
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
//
// Loop through Detected XML Files ...
foreach (var xmlFile in xmlFiles)
{
//
try
{
//
// Retrieve file name for Assembly Checking ...
var assemblyName = Path.GetFileNameWithoutExtension(xmlFile);
//
// Try to retrieve Assembly ...
var assembly = assemblies
.FirstOrDefault(a => a.GetName().Name == assemblyName);
if (!assembly.IsNullOrDefault() ||
File.Exists(Path.Combine(basePath, $"{assemblyName}.dll"))
)
{
//
// Includes Selected XML Comments ...
source.IncludeXmlComments(xmlFile);
}
}
catch (Exception ex)
{
Console.WriteLine($"Warning: Could not include XML comments from {xmlFile}. Error: {ex.Message}");
}
}
}
/// <summary> /// <summary>
/// Add Required OperationFilters and Authorization Fields to Swagger Options /// Add Required OperationFilters and Authorization Fields to Swagger Options
/// </summary> /// </summary>
@@ -52,37 +101,48 @@ namespace xCommons.Extensions {
bool addRequiredXPoweredFilter = true, bool addRequiredXPoweredFilter = true,
bool addXTokenAuthorization = true, bool addXTokenAuthorization = true,
bool addDefaultValueFilter = true bool addDefaultValueFilter = true
) { )
{
// //
if (source.IsNull ()) { if (source.IsNull())
{
source = new SwaggerGenOptions(); source = new SwaggerGenOptions();
} }
// //
// Add Xml File Path ... // Add Xml File Path ...
if (!xmlFilePath.IsNullOrEmpty ()) { if (!xmlFilePath.IsNullOrEmpty())
{
source.IncludeXmlComments(xmlFilePath); source.IncludeXmlComments(xmlFilePath);
} }
//
// Include all XML Documents ...
source.IncludeAssembliesXMLComments();
// //
// Handle RequireXPowered Filter ... // Handle RequireXPowered Filter ...
if (addRequiredXPoweredFilter) { if (addRequiredXPoweredFilter)
{
source.OperationFilter<RequireXPoweredOperationFilter>(); source.OperationFilter<RequireXPoweredOperationFilter>();
} }
// //
// Add Default Values Filter ... // Add Default Values Filter ...
if (addDefaultValueFilter) { if (addDefaultValueFilter)
{
source.OperationFilter<SwaggerDefaultValuesOperationFilter>(); source.OperationFilter<SwaggerDefaultValuesOperationFilter>();
} }
// //
// Handle XToken Authorizations ... // Handle XToken Authorizations ...
if (addXTokenAuthorization) { if (addXTokenAuthorization)
{
// //
#region Bearer AccessToken ... #region Bearer AccessToken ...
// //
source.AddSecurityDefinition (nameof (XAuthorization.AccessToken), new OpenApiSecurityScheme { source.AddSecurityDefinition(nameof(XAuthorization.AccessToken), new OpenApiSecurityScheme
{
In = ParameterLocation.Header, In = ParameterLocation.Header,
Description = "Bearer token", Description = "Bearer token",
Type = SecuritySchemeType.ApiKey, Type = SecuritySchemeType.ApiKey,
@@ -105,7 +165,8 @@ namespace xCommons.Extensions {
// //
#region RefreshToken ... #region RefreshToken ...
// //
source.AddSecurityDefinition (nameof (XAuthorization.RefreshToken), new OpenApiSecurityScheme { source.AddSecurityDefinition(nameof(XAuthorization.RefreshToken), new OpenApiSecurityScheme
{
In = ParameterLocation.Header, In = ParameterLocation.Header,
Description = "refresh token", Description = "refresh token",
Type = SecuritySchemeType.ApiKey, Type = SecuritySchemeType.ApiKey,
@@ -128,7 +189,8 @@ namespace xCommons.Extensions {
// //
#region ExpiresAt ... #region ExpiresAt ...
// //
source.AddSecurityDefinition (nameof (XAuthorization.ExpiresAt), new OpenApiSecurityScheme { source.AddSecurityDefinition(nameof(XAuthorization.ExpiresAt), new OpenApiSecurityScheme
{
In = ParameterLocation.Header, In = ParameterLocation.Header,
Description = "expires at", Description = "expires at",
Type = SecuritySchemeType.ApiKey, Type = SecuritySchemeType.ApiKey,
@@ -168,10 +230,12 @@ namespace xCommons.Extensions {
bool addRequiredXPoweredFilter = true, bool addRequiredXPoweredFilter = true,
bool addXTokenAuthorization = true, bool addXTokenAuthorization = true,
bool addDefaultValueFilter = true bool addDefaultValueFilter = true
) { )
{
// //
var xSwaggerConfig = configuration.GetXSwaggerConfiguration(); var xSwaggerConfig = configuration.GetXSwaggerConfiguration();
if (xSwaggerConfig.IsNull ()) { if (xSwaggerConfig.IsNull())
{
xSwaggerConfig = new XSwaggerConfiguration(); xSwaggerConfig = new XSwaggerConfiguration();
} }
@@ -180,7 +244,8 @@ namespace xCommons.Extensions {
// //
#region Prepare SwaggerGenOptions ... #region Prepare SwaggerGenOptions ...
if (settings.IsNull ()) { if (settings.IsNull())
{
settings = new SwaggerGenOptions(); settings = new SwaggerGenOptions();
} }
settings.AddXSwaggerGenOptions( settings.AddXSwaggerGenOptions(
@@ -195,7 +260,8 @@ namespace xCommons.Extensions {
#region Prepare Swagger Doc ... #region Prepare Swagger Doc ...
// //
// Api Document Section ... // Api Document Section ...
var apiDoc = new OpenApiInfo { var apiDoc = new OpenApiInfo
{
Title = xSwaggerConfig.Title, Title = xSwaggerConfig.Title,
Version = xSwaggerConfig.Version, Version = xSwaggerConfig.Version,
Description = xSwaggerConfig.Description, Description = xSwaggerConfig.Description,
@@ -203,22 +269,26 @@ namespace xCommons.Extensions {
// //
// Api Document TermsOfUse URL ... // Api Document TermsOfUse URL ...
if (!xSwaggerConfig.TermsOfServiceUrl.IsNullOrEmpty ()) { if (!xSwaggerConfig.TermsOfServiceUrl.IsNullOrEmpty())
{
apiDoc.TermsOfService = new Uri(xSwaggerConfig.TermsOfServiceUrl); apiDoc.TermsOfService = new Uri(xSwaggerConfig.TermsOfServiceUrl);
} }
// //
// Contact Section ... // Contact Section ...
if (!xSwaggerConfig.Contact.IsNull ()) { if (!xSwaggerConfig.Contact.IsNull())
{
// //
var apiContact = new OpenApiContact { var apiContact = new OpenApiContact
{
Name = xSwaggerConfig.Contact.Name, Name = xSwaggerConfig.Contact.Name,
Email = xSwaggerConfig.Contact.Email Email = xSwaggerConfig.Contact.Email
}; };
// //
// Contact URL ... // Contact URL ...
if (!xSwaggerConfig.Contact.Url.IsNullOrEmpty ()) { if (!xSwaggerConfig.Contact.Url.IsNullOrEmpty())
{
apiContact.Url = new Uri(xSwaggerConfig.Contact.Url); apiContact.Url = new Uri(xSwaggerConfig.Contact.Url);
} }
@@ -228,15 +298,18 @@ namespace xCommons.Extensions {
// //
// License Section ... // License Section ...
if (!xSwaggerConfig.License.IsNull ()) { if (!xSwaggerConfig.License.IsNull())
{
// //
var apiLicense = new OpenApiLicense { var apiLicense = new OpenApiLicense
{
Name = xSwaggerConfig.License.Name, Name = xSwaggerConfig.License.Name,
}; };
// //
// License URL ... // License URL ...
if (!xSwaggerConfig.License.Url.IsNullOrEmpty ()) { if (!xSwaggerConfig.License.Url.IsNullOrEmpty())
{
apiLicense.Url = new Uri(xSwaggerConfig.License.Url); apiLicense.Url = new Uri(xSwaggerConfig.License.Url);
} }
@@ -248,7 +321,8 @@ namespace xCommons.Extensions {
// //
// Register Swagger ... // Register Swagger ...
source.AddSwaggerGen( source.AddSwaggerGen(
opt => { opt =>
{
// //
opt.UpdateData(settings); opt.UpdateData(settings);
@@ -269,14 +343,18 @@ namespace xCommons.Extensions {
public static void UseXSwagger( public static void UseXSwagger(
this IApplicationBuilder source, this IApplicationBuilder source,
SwaggerUIOptions options = null SwaggerUIOptions options = null
) { )
{
// //
source.UseSwagger(); source.UseSwagger();
// //
if (options.IsNull ()) { if (options.IsNull())
{
source.UseSwaggerUI(); source.UseSwaggerUI();
} else { }
else
{
source.UseSwaggerUI(options: options); source.UseSwaggerUI(options: options);
} }
} }
@@ -289,16 +367,20 @@ namespace xCommons.Extensions {
public static void AddXCors( public static void AddXCors(
this IServiceCollection source, this IServiceCollection source,
IEnumerable<string> allowedOrigins IEnumerable<string> allowedOrigins
) { )
{
// //
if (!allowedOrigins.HasChild ()) { if (!allowedOrigins.HasChild())
{
// //
Console.WriteLine($"XCommons: there is no provided allowedOrigins, start app without any specific cors, this may be unsecure ..."); Console.WriteLine($"XCommons: there is no provided allowedOrigins, start app without any specific cors, this may be unsecure ...");
// //
source.AddCors (options => { source.AddCors(options =>
{
options.AddPolicy(XPolicy.AllowedOrigins, options.AddPolicy(XPolicy.AllowedOrigins,
builder => { builder =>
{
builder builder
.AllowAnyOrigin() .AllowAnyOrigin()
.AllowAnyHeader() .AllowAnyHeader()
@@ -312,9 +394,11 @@ namespace xCommons.Extensions {
} }
// //
source.AddCors (options => { source.AddCors(options =>
{
options.AddPolicy(XPolicy.AllowedOrigins, options.AddPolicy(XPolicy.AllowedOrigins,
builder => { builder =>
{
builder builder
.WithOrigins(allowedOrigins.ToArray()) .WithOrigins(allowedOrigins.ToArray())
.AllowAnyHeader() .AllowAnyHeader()
@@ -329,7 +413,8 @@ namespace xCommons.Extensions {
/// Use X Registered Cross Origin Resource Sharings /// Use X Registered Cross Origin Resource Sharings
/// </summary> /// </summary>
/// <param name="sources"></param> /// <param name="sources"></param>
public static void UseXCors (this IApplicationBuilder sources) { public static void UseXCors(this IApplicationBuilder sources)
{
// //
sources.UseCors(XPolicy.AllowedOrigins); sources.UseCors(XPolicy.AllowedOrigins);
} }
+2 -2
View File
@@ -65,9 +65,9 @@
<!-- For XML Documentation Support --> <!-- For XML Documentation Support -->
<PropertyGroup> <PropertyGroup>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn> <NoWarn>$(NoWarn);1591</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>
</Project> </Project>