swagger extensions

messaging
Tudor Stanciu 2020-12-20 22:31:53 +02:00
parent 38be5dedaf
commit c2c81a7000
7 changed files with 199 additions and 11 deletions

View File

@ -1,8 +0,0 @@
using System;
namespace NDB.Extensions.Swagger
{
public class Class1
{
}
}

View File

@ -0,0 +1,8 @@
namespace NDB.Extensions.Swagger.Constants
{
public enum AuthorizationType
{
None,
Basic
}
}

View File

@ -1,6 +1,7 @@
using System; using Microsoft.OpenApi.Models;
using System.Collections.Generic; using NDB.Application.DataContracts;
using System.Text; using Swashbuckle.AspNetCore.SwaggerGen;
using System.Linq;
namespace NDB.Extensions.Swagger.Filters namespace NDB.Extensions.Swagger.Filters
{ {

View File

@ -0,0 +1,42 @@
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Linq;
using System.Text.RegularExpressions;
namespace NDB.Extensions.Swagger.Filters
{
public class PathParamsOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
const string paramCaptureGroup = "param";
foreach (var parameter in operation.Parameters.ToList())
{
if (parameter.Name.ToLowerInvariant().StartsWith("metadata"))
{
operation.Parameters.Remove(parameter);
}
}
var openApiPathParameters = operation.Parameters.Where(param => param.In == ParameterLocation.Path).ToList();
var pathParamRegEx = $@"\{{(?<{paramCaptureGroup}>[^\}}]+)\}}";
if (openApiPathParameters.Any())
{
var pathParameterMatches = Regex.Matches(context.ApiDescription.RelativePath, pathParamRegEx, RegexOptions.Compiled);
var pathParameters = pathParameterMatches.Cast<Match>().Select(x => x.Groups[paramCaptureGroup].Value);
foreach (var openApiPathParameter in openApiPathParameters)
{
var correspondingPathParameter = pathParameters.FirstOrDefault(x =>
string.Equals(x, openApiPathParameter.Name, StringComparison.InvariantCultureIgnoreCase));
if (correspondingPathParameter != null)
openApiPathParameter.Name = correspondingPathParameter;
}
}
}
}
}

View File

@ -4,4 +4,9 @@
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="NDB.Application.DataContracts" Version="1.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.3.1" />
</ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,38 @@
using Microsoft.AspNetCore.Http;
using System.Linq;
namespace NDB.Extensions.Swagger.ReverseProxy
{
public static class ReverseProxyHelper
{
public static (string host, string basePath, string scheme) GetUrlComponents(HttpRequest request)
{
var host = ExtractHost(request);
var basePath = ExtractBasePath(request);
var scheme = ExtractScheme(request);
return (host, basePath, scheme);
}
private static string ExtractHost(HttpRequest request)
{
if (request.Headers.ContainsKey("X-Forwarded-Host"))
return request.Headers["X-Forwarded-Host"].First();
return request.Host.Value;
}
private static string ExtractBasePath(HttpRequest request)
{
if (request.Headers.ContainsKey("X-Forwarded-PathBase"))
return request.Headers["X-Forwarded-PathBase"].First();
return string.Empty;
}
private static string ExtractScheme(HttpRequest request)
{
return request.Headers["X-Forwarded-Proto"].FirstOrDefault() ?? request.Scheme;
}
}
}

View File

@ -0,0 +1,102 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using NDB.Extensions.Swagger.Constants;
using NDB.Extensions.Swagger.Filters;
using NDB.Extensions.Swagger.ReverseProxy;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Collections.Generic;
namespace NDB.Extensions.Swagger
{
public static class SwaggerExtensions
{
public static IServiceCollection AddSwagger(this IServiceCollection services, string title, AuthorizationType authorizationType = AuthorizationType.Basic)
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1",
new OpenApiInfo
{
Title = title,
Version = "v1"
});
c.SetAuthorization(authorizationType);
c.OperationFilter<PathParamsOperationFilter>();
c.SchemaFilter<DtoSchemaFilter>();
c.CustomSchemaIds(type => type.ToString());
});
return services;
}
private static void SetAuthorization(this SwaggerGenOptions options, AuthorizationType authorizationType)
{
switch (authorizationType)
{
case AuthorizationType.None:
return;
case AuthorizationType.Basic:
options.AddSecurityDefinition("Basic",
new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Description = @"JWT Authorization header using the Basic scheme. Enter 'Basic' [space] and then your token in the text input below. Example: 'Basic 12345abcdef'",
Name = "Authorization",
Scheme = "Basic",
Type = SecuritySchemeType.ApiKey
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Basic"
},
Scheme = "Basic",
Name = "Authorization",
In = ParameterLocation.Header
},
new List<string>()
}
});
break;
default:
throw new NotImplementedException($"Swagger extensions: Authorization type '{authorizationType}' is not implemented.");
}
}
public static IApplicationBuilder ConfigureSwagger(this IApplicationBuilder applicationBuilder, string endpointName)
{
applicationBuilder.UseSwagger(c =>
{
c.PreSerializeFilters.Add((swagger, httpRequest) =>
{
var (host, basePath, scheme) = ReverseProxyHelper.GetUrlComponents(httpRequest);
swagger.Servers = new List<OpenApiServer>
{
new OpenApiServer {Url = $"{scheme}://{host}{basePath}"}
};
});
c.RouteTemplate = "swagger/{documentName}/swagger.json";
});
applicationBuilder.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("v1/swagger.json", endpointName);
c.RoutePrefix = $"swagger";
});
return applicationBuilder;
}
}
}