Swagger working but with removed api versioning system and a lot of other shit removed during diagnosis. Revert to this commit to go that route.

This commit is contained in:
2019-10-17 17:50:21 +00:00
parent 1ff256029f
commit c9155ff24e
27 changed files with 385 additions and 282 deletions

View File

@@ -11,6 +11,29 @@ UPDATE all the things before commencing work
- https://github.com/domaindrivendev/Swashbuckle.AspNetCore#swashbuckleaspnetcoreannotations
-
2019-10-16 12:47:22.3600|ERROR|Microsoft.AspNetCore.Server.Kestrel|Connection id "0HLQIE5LC01UO", Request id "0HLQIE5LC01UO:00000003":
An unhandled exception was thrown by the application.=>System.InvalidOperationException: Endpoint AyaNova.Api.Controllers.WidgetController.ListWidgets (AyaNova)
contains authorization metadata, but a middleware was not found that supports authorization.
Configure your application startup by adding app.UseAuthorization() inside the call to Configure(..) in the application startup code. The call to app.UseAuthorization() must appear between app.UseRouting() and app.UseEndpoints(...).
at Microsoft.AspNetCore.Routing.EndpointMiddleware.ThrowMissingAuthMiddlewareException(Endpoint endpoint)
2019-10-16 12:45:37.5732|ERROR|Microsoft.AspNetCore.Server.Kestrel|Connection id "0HLQIE5LC01UI", Request id "0HLQIE5LC01UI:00000001":
An unhandled exception was thrown by the application.=>System.TypeLoadException:
Could not load type 'Microsoft.AspNetCore.Mvc.Internal.MvcAttributeRouteHandler' from assembly 'Microsoft.AspNetCore.Mvc.Core, Version=3.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.
at Microsoft.AspNetCore.Mvc.Internal.MvcRouteTemplateResolver.ResolveMatchingTemplateRouteAsync(RouteData routeData)
Need a sprint to get to a fully testable client with entry form, list and as much as possible all features from COMMON-* specs list
Do the stuff in the Client todo first then back to the server as required.

View File

@@ -32,22 +32,18 @@
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.0-preview1.19508.20" />
<PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="3.1.0-preview1.19508.20" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.0-preview1.19508.20" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="4.0.0-preview8.19405.7" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="4.0.0-preview8.19405.7" />
<!-- <PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="4.0.0-preview8.19405.7" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="4.0.0-preview8.19405.7" /> -->
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0-preview1.19506.2" />
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="3.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="3.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.OpenApi" Version="1.1.4" />
<PackageReference Include="NLog" Version="4.6.7" />
<PackageReference Include="NLog.Web.AspNetCore" Version="4.9.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc4" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="5.0.0-rc4" />
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="5.0.0-rc4" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.0.0-rc4" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUi" Version="5.0.0-rc4" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc4" />
</ItemGroup>
<Target Name="CopyCustomContent" AfterTargets="AfterBuild">

View File

@@ -4,14 +4,16 @@ using System;
using AyaNova.Util;
using AyaNova.Biz;
using AyaNova.Api.ControllerHelpers;
using Microsoft.AspNetCore.Authorization;
namespace AyaNova.Api.Controllers
{
/// <summary>
/// Meta controller class
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/")]
[AllowAnonymous]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/")]//was [Route("api/v{version:apiVersion}/")]
public class ApiMetaController : Controller
{
private readonly ApiServerState serverState;

View File

@@ -32,8 +32,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// Attachment controller
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class AttachmentController : Controller

View File

@@ -10,6 +10,7 @@ using System;
using System.Threading.Tasks;
using App.Metrics;
using AyaNova.Biz;
using Microsoft.AspNetCore.Authorization;
//required to inject configuration in constructor
using Microsoft.Extensions.Configuration;
@@ -19,9 +20,10 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// Authentication controller
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[AllowAnonymous]
public class AuthController : Controller
{
private readonly AyContext ct;

View File

@@ -15,8 +15,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// Enum pick list controller
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class AyaEnumPickListController : Controller

View File

@@ -15,8 +15,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// AyaType list controller
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class AyaTypeController : Controller

View File

@@ -46,8 +46,8 @@ namespace AyaNova.Api.Controllers
/// and triggering a restore from backup
///
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class BackupController : Controller

View File

@@ -19,8 +19,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
///
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class DataFilterController : Controller

View File

@@ -19,8 +19,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// Log files controller
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Authorize]
public class EventLogController : Controller
{

View File

@@ -19,8 +19,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
///
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class FormCustomController : Controller

View File

@@ -29,8 +29,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// Import AyaNova 7 data controller
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class ImportAyaNova7Controller : Controller

View File

@@ -17,8 +17,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// JobOperations controller
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class JobOperationsController : Controller

View File

@@ -16,8 +16,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// License route
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class LicenseController : Controller

View File

@@ -23,8 +23,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// Localized text controller
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class LocaleController : Controller

View File

@@ -16,8 +16,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// Log files controller
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
//[Produces("application/json")]
[Authorize]
public class LogFilesController : Controller

View File

@@ -18,8 +18,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// Log files controller
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Authorize]
public class MetricsController : Controller
{

View File

@@ -19,8 +19,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// Search
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class SearchController : Controller

View File

@@ -15,8 +15,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// Server state controller
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
public class ServerStateController : Controller
{

View File

@@ -15,8 +15,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// Enum pick list controller
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class TagListController : Controller

View File

@@ -13,8 +13,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
///Test controller class used during development
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class TrialController : Controller

View File

@@ -19,8 +19,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// User
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class UserController : Controller

View File

@@ -19,8 +19,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// UserOptions
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class UserOptionsController : Controller

View File

@@ -22,8 +22,8 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// Sample controller class used during development for testing purposes
/// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/[controller]")]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")]
[Authorize]
public class WidgetController : Controller

View File

@@ -207,45 +207,45 @@ namespace AyaNova
.UseUrls(ServerBootConfig.AYANOVA_USE_URLS)//default port and urls, set first can be overridden by any later setting here
.UseConfiguration(configuration)//support command line override of port (dotnet run urls=http://*:8081)
.UseIISIntegration()//support IIS integration just in case, it appears here to override prior settings if necessary (port)
.ConfigureMetricsWithDefaults(builder =>
{
if (ServerBootConfig.AYANOVA_METRICS_USE_INFLUXDB)
{
builder.Report.ToInfluxDb(
options =>
{
// .ConfigureMetricsWithDefaults(builder =>
// {
// if (ServerBootConfig.AYANOVA_METRICS_USE_INFLUXDB)
// {
// builder.Report.ToInfluxDb(
// options =>
// {
options.InfluxDb.BaseUri = new Uri(ServerBootConfig.AYANOVA_METRICS_INFLUXDB_BASEURL);
options.InfluxDb.Database = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_DBNAME;
if (!string.IsNullOrWhiteSpace(ServerBootConfig.AYANOVA_METRICS_INFLUXDB_CONSISTENCY))
{
options.InfluxDb.Consistenency = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_CONSISTENCY;
}
options.InfluxDb.UserName = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_USERNAME;
options.InfluxDb.Password = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_PASSWORD;
if (!string.IsNullOrWhiteSpace(ServerBootConfig.AYANOVA_METRICS_INFLUXDB_RETENTION_POLICY))
{
options.InfluxDb.RetentionPolicy = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_RETENTION_POLICY;
}
// options.InfluxDb.BaseUri = new Uri(ServerBootConfig.AYANOVA_METRICS_INFLUXDB_BASEURL);
// options.InfluxDb.Database = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_DBNAME;
// if (!string.IsNullOrWhiteSpace(ServerBootConfig.AYANOVA_METRICS_INFLUXDB_CONSISTENCY))
// {
// options.InfluxDb.Consistenency = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_CONSISTENCY;
// }
// options.InfluxDb.UserName = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_USERNAME;
// options.InfluxDb.Password = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_PASSWORD;
// if (!string.IsNullOrWhiteSpace(ServerBootConfig.AYANOVA_METRICS_INFLUXDB_RETENTION_POLICY))
// {
// options.InfluxDb.RetentionPolicy = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_RETENTION_POLICY;
// }
options.InfluxDb.CreateDataBaseIfNotExists = true;
options.HttpPolicy.BackoffPeriod = TimeSpan.FromSeconds(30);
options.HttpPolicy.FailuresBeforeBackoff = 5;
options.HttpPolicy.Timeout = TimeSpan.FromSeconds(10);
//options.MetricsOutputFormatter = new App.Metrics.Formatters.Json.MetricsJsonOutputFormatter();
//options.Filter = filter;
options.FlushInterval = TimeSpan.FromSeconds(20);
});
}
// options.InfluxDb.CreateDataBaseIfNotExists = true;
// options.HttpPolicy.BackoffPeriod = TimeSpan.FromSeconds(30);
// options.HttpPolicy.FailuresBeforeBackoff = 5;
// options.HttpPolicy.Timeout = TimeSpan.FromSeconds(10);
// //options.MetricsOutputFormatter = new App.Metrics.Formatters.Json.MetricsJsonOutputFormatter();
// //options.Filter = filter;
// options.FlushInterval = TimeSpan.FromSeconds(20);
// });
// }
})
.UseMetricsEndpoints(opt =>
{
opt.EnvironmentInfoEndpointEnabled = false;
opt.MetricsEndpointEnabled = false;
opt.MetricsTextEndpointEnabled = false;
})
.UseMetrics()
// })
// .UseMetricsEndpoints(opt =>
// {
// opt.EnvironmentInfoEndpointEnabled = false;
// opt.MetricsEndpointEnabled = false;
// opt.MetricsTextEndpointEnabled = false;
// })
// .UseMetrics()
.UseStartup<Startup>()
.ConfigureLogging((context, logging) =>
{

View File

@@ -62,23 +62,31 @@ namespace AyaNova
{
_log.LogDebug("BOOT: initializing services...");
//dotnet 3.x added this wasn't here before
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
});
//Server state service for shutting people out of api
_log.LogDebug("BOOT: init ApiServerState service");
services.AddSingleton(new AyaNova.Api.ControllerHelpers.ApiServerState());
//dotnet 3 commented this out, new project doesn't have it
//Init mvc
_log.LogDebug("BOOT: init MVC Core service");
var mvc = services.AddMvcCore();
_log.LogDebug("BOOT: add json service");
mvc.AddNewtonsoftJson();
// _log.LogDebug("BOOT: init MVC Core service");
// var mvc = services.AddMvcCore();
// _log.LogDebug("BOOT: add json service");
// mvc.AddNewtonsoftJson();
// add the versioned api explorer, which also adds IApiVersionDescriptionProvider service
// note: the specified format code will format the version as "'v'major[.minor][-status]"
_log.LogDebug("BOOT: init ApiExplorer service");
services.AddVersionedApiExplorer(o => o.GroupNameFormat = "'v'VVV");
// services.AddVersionedApiExplorer(o => o.GroupNameFormat = "'v'VVV");
_log.LogDebug("BOOT: ensuring user and backup folders exist and are separate locations...");
FileUtil.EnsureUserAndUtilityFoldersExistAndAreNotIdentical(_hostingEnvironment.ContentRootPath);
@@ -135,44 +143,45 @@ namespace AyaNova
#endregion
_log.LogDebug("BOOT: init ApiVersioning service");
// services.AddApiVersioning(o => o.ReportApiVersions = true);
services
.AddApiVersioning(options =>
{
options.AssumeDefaultVersionWhenUnspecified = true;
options.DefaultApiVersion = Microsoft.AspNetCore.Mvc.ApiVersion.Parse("8.0");
options.ReportApiVersions = true;
});
// services
// .AddApiVersioning(options =>
// {
// options.AssumeDefaultVersionWhenUnspecified = true;
// options.DefaultApiVersion = Microsoft.AspNetCore.Mvc.ApiVersion.Parse("8.0");
// options.ReportApiVersions = true;
// });
// Add service and create Policy with options
_log.LogDebug("BOOT: init CORS service");
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
//v3 commented out to try to figure out why the swagger docs aren't generating and may be superfluous
// // Add service and create Policy with options
// _log.LogDebug("BOOT: init CORS service");
// services.AddCors(options =>
// {
// options.AddPolicy("CorsPolicy",
// builder => builder.AllowAnyOrigin()
// .AllowAnyMethod()
// .AllowAnyHeader()
// //.AllowCredentials()
// );
// });
_log.LogDebug("BOOT: init MVC service");
_log.LogDebug("BOOT: init Metrics service");
services.AddMvc(config =>
{
//was this but needed logging, not certain about the new way of adding so keeping this in case it all goes sideways in testing
//config.Filters.Add(typeof(AyaNova.Api.ControllerHelpers.ApiCustomExceptionFilter));
config.Filters.Add(new AyaNova.Api.ControllerHelpers.ApiCustomExceptionFilter(AyaNova.Util.ApplicationLogging.LoggerFactory));
// services.AddMvc(config =>
// {
// //was this but needed logging, not certain about the new way of adding so keeping this in case it all goes sideways in testing
// //config.Filters.Add(typeof(AyaNova.Api.ControllerHelpers.ApiCustomExceptionFilter));
// config.Filters.Add(new AyaNova.Api.ControllerHelpers.ApiCustomExceptionFilter(AyaNova.Util.ApplicationLogging.LoggerFactory));
}).AddMetrics().AddJsonOptions(options =>
{
//2019-10-15 - removed this due to fuckery after update, is it required??? Not sure at this point
//if metrics have wrong dates then it's important I guess
//options.JsonSerializerOptions.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
});
// }).AddMetrics().AddJsonOptions(options =>
// {
// //2019-10-15 - removed this due to fuckery after update, is it required??? Not sure at this point
// //if metrics have wrong dates then it's important I guess
// //options.JsonSerializerOptions.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
// });
#region Swagger
@@ -181,73 +190,112 @@ namespace AyaNova
//https://github.com/domaindrivendev/Swashbuckle.AspNetCore
_log.LogDebug("BOOT: init API explorer service");
services.AddSwaggerGen(
c =>
{
// resolve the IApiVersionDescriptionProvider service
// note: that we have to build a temporary service provider here because one has not been created yet
var provider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>();
// services.AddSwaggerGen(c =>
// {
// c.SwaggerDoc("v8", new OpenApiInfo { Title = "My API", Version = "v8" });
// c.OperationFilter<AuthResponsesOperationFilter>();
// add a swagger document for each discovered API version
// note: you might choose to skip or document deprecated API versions differently
foreach (var description in provider.ApiVersionDescriptions)
{
c.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
}
// });
// services.AddSwaggerGen(
// c =>
// {
// add a custom operation filter which sets default values
//Removed because will no longer compile the SwaggerDefaultValues but may be needed, not sure it's all a bit muddled
// c.OperationFilter<SwaggerDefaultValues>();
// integrate xml comments
c.IncludeXmlComments(XmlCommentsFilePath);
// c.DocInclusionPredicate((docName, apiDesc) =>
// {
// if (!apiDesc.TryGetMethodInfo(out MethodInfo methodInfo)) return false;
// // Get the MapToApiVersion attributes of the action
// var mapApiVersions = methodInfo
// .GetCustomAttributes(true)
// .OfType<MapToApiVersionAttribute>()
// .SelectMany(attr => attr.Versions);
// //if it contains MapToApiVersion attributes, then we should check those as the ApiVersion ones are ignored
// if (mapApiVersions.Any() && mapApiVersions.Any(v => $"v{v.ToString()}" == docName))
// return true;
// // Get the ApiVersion attributes of the controller
// var versions = methodInfo.DeclaringType
// .GetCustomAttributes(true)
// .OfType<ApiVersionAttribute>()
// .SelectMany(attr => attr.Versions);
// return versions.Any(v => $"v{v.ToString()}" == docName);
// });
//2019-10-15 - Removed this because apikeyscheme is no longer recognized and it appears it *may* not be necessary... TWT
//If I have any issues with bearer tokens in swagger then this is probably necessary but in a new way
//this is required to allow authentication when testing secure routes via swagger UI
// c.AddSecurityDefinition("Bearer", new ApiKeyScheme
// {
// Description = "JWT Authorization header using the Bearer scheme. Get your token by logging in via the Auth route then enter it here with the \"Bearer \" prefix. Example: \"Bearer {token}\"",
// Name = "Authorization",
// In = "header",
// Type = "apiKey"
// // resolve the IApiVersionDescriptionProvider service
// // note: that we have to build a temporary service provider here because one has not been created yet
// var provider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>();
// });
// // add a swagger document for each discovered API version
// // note: you might choose to skip or document deprecated API versions differently
// foreach (var description in provider.ApiVersionDescriptions)
// {
// c.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
// }
// // add a custom operation filter which sets default values
// // c.OperationFilter<SwaggerDefaultValues>();
// //test filter WTF?
// //c.OperationFilter<AuthResponsesOperationFilter>();
// // integrate xml comments
// c.IncludeXmlComments(XmlCommentsFilePath);
// // //2019-10-15 - Removed this because apikeyscheme is no longer recognized and it appears it *may* not be necessary... TWT
// // //If I have any issues with bearer tokens in swagger then this is probably necessary but in a new way
// // //this is required to allow authentication when testing secure routes via swagger UI
// // c.AddSecurityDefinition("Bearer", new ApiKeyScheme
// // {
// // Description = "JWT Authorization header using the Bearer scheme. Get your token by logging in via the Auth route then enter it here with the \"Bearer \" prefix. Example: \"Bearer {token}\"",
// // Name = "Authorization",
// // In = "header",
// // Type = "apiKey"
// // });
//Obsolete way
// c.AddSecurityRequirement(new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>
// {
// { "Bearer", new string[] { } }
// });
// //Obsolete way
// // c.AddSecurityRequirement(new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>
// // {
// // { "Bearer", new string[] { } }
// // });
//https://stackoverflow.com/questions/56234504/migrating-to-swashbuckle-aspnetcore-version-5
//First we define the security scheme
c.AddSecurityDefinition("Bearer", //Name the security scheme
new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme.",
Type = SecuritySchemeType.Http, //We set the scheme type to http since we're using bearer authentication
Scheme = "bearer" //The name of the HTTP Authorization scheme to be used in the Authorization header. In this case "bearer".
});
// //https://stackoverflow.com/questions/56234504/migrating-to-swashbuckle-aspnetcore-version-5
// //First we define the security scheme
// c.AddSecurityDefinition("Bearer", //Name the security scheme
// new OpenApiSecurityScheme
// {
// Description = "JWT Authorization header using the Bearer scheme.",
// Type = SecuritySchemeType.Http, //We set the scheme type to http since we're using bearer authentication
// Scheme = "bearer" //The name of the HTTP Authorization scheme to be used in the Authorization header. In this case "bearer".
// });
c.AddSecurityRequirement(new OpenApiSecurityRequirement{
{
new OpenApiSecurityScheme{
Reference = new OpenApiReference{
Id = "Bearer", //The name of the previously defined security scheme.
Type = ReferenceType.SecurityScheme
}
},new List<string>()
}
});
// c.AddSecurityRequirement(new OpenApiSecurityRequirement{
// {
// new OpenApiSecurityScheme{
// Reference = new OpenApiReference{
// Id = "Bearer", //The name of the previously defined security scheme.
// Type = ReferenceType.SecurityScheme
// }
// },new List<string>()
// }
// });
});
// //https://github.com/domaindrivendev/Swashbuckle.AspNetCore
// //ARGGHHHHHHHH!!!!!
// });
#endregion
@@ -319,10 +367,19 @@ namespace AyaNova
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
//
public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IWebHostEnvironment env,
AyContext dbContext, IApiVersionDescriptionProvider provider, AyaNova.Api.ControllerHelpers.ApiServerState apiServerState, IServiceProvider serviceProvider)
AyContext dbContext, AyaNova.Api.ControllerHelpers.ApiServerState apiServerState, IServiceProvider serviceProvider)
{
//This was in constructor right after dbcontext: IApiVersionDescriptionProvider provider,
_log.LogDebug("BOOT: configuring request pipeline...");
//dotnet3 test added
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
//This is in new templates generated for webapi but it errors out for me so removing it for now
// app.UseHttpsRedirection();
//Store a reference to the dependency injection service for static classes
ServiceProviderProvider.Provider = app.ApplicationServices;
@@ -337,26 +394,15 @@ namespace AyaNova
_log.LogDebug("BOOT: pipeline - static files");
app.UseDefaultFiles();
app.UseStaticFiles();
//Might need the following if the page doesn't update in the client properly
//however the vue build process will automatically uniquify each build file names so maybe not required
// app.UseStaticFiles(new StaticFileOptions
// {
// OnPrepareResponse = context =>
// {
// if (context.File.Name == "index.html")
// {
// context.Context.Response.Headers.Add("Cache-Control", "no-cache, no-store");
// context.Context.Response.Headers.Add("Expires", "-1");
// }
// }
// });
#endregion
_log.LogDebug("BOOT: pipeline - ROUTING");
app.UseRouting();//this wasn't here for 2.2 but added for 3.0, needs to come before the stuff after
_log.LogDebug("BOOT: pipeline - CORS");
app.UseCors("CorsPolicy");
//v3test
// _log.LogDebug("BOOT: pipeline - CORS");
// app.UseCors("CorsPolicy");
#region AUTH / ROLES
@@ -364,6 +410,9 @@ namespace AyaNova
//Use authentication middleware
app.UseAuthentication();
_log.LogDebug("BOOT: pipeline - authorization");
app.UseAuthorization();
//Custom middleware to get user roles and put them into the request so
//they can be authorized in routes.
app.Use(async (context, next) =>
@@ -395,28 +444,46 @@ namespace AyaNova
#endregion
//According to https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-2.2&tabs=visual-studio#migrate-startupconfigure
_log.LogDebug("BOOT: pipeline - ENDPOINTS");
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
#region SWAGGER
_log.LogDebug("BOOT: pipeline - api explorer");
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// // Enable middleware to serve generated Swagger as a JSON endpoint.
// app.UseSwagger();
// app.UseSwaggerUI(c =>
// {
// // build a swagger endpoint for each discovered API version
// foreach (var description in provider.ApiVersionDescriptions)
// {
// c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
// }
// //clean up the swagger explorer UI page and remove the branding
// //via our own css
// //NOTE: this broke when updated to v2.x of swagger and it can be fixed according to docs:
// //https://github.com/domaindrivendev/Swashbuckle.AspNetCore#inject-custom-css
// // c.InjectStylesheet("/api/sw.css");
// c.DefaultModelsExpandDepth(-1);
// c.DocumentTitle = "AyaNova API explorer";
// c.RoutePrefix = "api-docs";
// });
app.UseSwagger();
app.UseSwaggerUI(c =>
{
// build a swagger endpoint for each discovered API version
foreach (var description in provider.ApiVersionDescriptions)
{
c.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
}
//clean up the swagger explorer UI page and remove the branding
//via our own css
//NOTE: this broke when updated to v2.x of swagger and it can be fixed according to docs:
//https://github.com/domaindrivendev/Swashbuckle.AspNetCore#inject-custom-css
// c.InjectStylesheet("/api/sw.css");
c.DefaultModelsExpandDepth(-1);
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
c.DefaultModelsExpandDepth(-1);
c.DocumentTitle = "AyaNova API explorer";
c.RoutePrefix = "api-docs";
});
@@ -427,21 +494,12 @@ namespace AyaNova
//According to https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-2.2&tabs=visual-studio#migrate-startupconfigure
//replace use mvc with use endpoints
// //USE MVC
// _log.LogDebug("BOOT: pipeline - MVC");
// app.UseMvc();
_log.LogDebug("BOOT: pipeline - ENDPOINTS");
app.UseEndpoints(endpoints => {
endpoints.MapControllers();
});
// ******************************************************************
// ******************** TESTING WIPE DB *****************************
//
//Set this to true to wipe the db and reinstall a trial license and re-seed the data
var TESTING_REFRESH_DB = true;//#######################################################################################
var TESTING_REFRESH_DB = false;//#######################################################################################
#if (DEBUG)
//TESTING
@@ -524,33 +582,33 @@ _log.LogDebug("BOOT: pipeline - ENDPOINTS");
#region Swagger and API Versioning utilities
static string XmlCommentsFilePath
{
get
{
//Obsolete, used new method: https://developers.de/blogs/holger_vetter/archive/2017/06/30/swagger-includexmlcomments-platformservices-obsolete-replacement.aspx
//var basePath = PlatformServices.Default.Application.ApplicationBasePath;
var basePath = AppContext.BaseDirectory;
var fileName = typeof(Startup).GetTypeInfo().Assembly.GetName().Name + ".xml";
return Path.Combine(basePath, fileName);
}
}
// static string XmlCommentsFilePath
// {
// get
// {
// //Obsolete, used new method: https://developers.de/blogs/holger_vetter/archive/2017/06/30/swagger-includexmlcomments-platformservices-obsolete-replacement.aspx
// //var basePath = PlatformServices.Default.Application.ApplicationBasePath;
// var basePath = AppContext.BaseDirectory;
// var fileName = typeof(Startup).GetTypeInfo().Assembly.GetName().Name + ".xml";
// return Path.Combine(basePath, fileName);
// }
// }
static Microsoft.OpenApi.Models.OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
{
var info = new Microsoft.OpenApi.Models.OpenApiInfo()
{
Title = $"AyaNova API {description.ApiVersion}",
Version = description.ApiVersion.ToString()
};
// static Microsoft.OpenApi.Models.OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
// {
// var info = new Microsoft.OpenApi.Models.OpenApiInfo()
// {
// Title = $"AyaNova API {description.ApiVersion}",
// Version = description.ApiVersion.ToString()
// };
if (description.IsDeprecated)
{
info.Description += " This API version has been deprecated.";
}
// if (description.IsDeprecated)
// {
// info.Description += " This API version has been deprecated.";
// }
return info;
}
// return info;
// }
#endregion

View File

@@ -1,47 +1,69 @@
// namespace AyaNova
// {
// using Swashbuckle.AspNetCore.SwaggerGen;
// using System.Linq;
namespace AyaNova
{
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Linq;
using Microsoft.AspNetCore.Authorization;
//https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/tag/v5.0.0-rc3
/// <summary>
/// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter.
/// </summary>
/// <remarks>This <see cref="IOperationFilter"/> is only required due to bugs in the <see cref="SwaggerGenerator"/>.
/// Once they are fixed and published, this class can be removed.</remarks>
public class SwaggerDefaultValues : IOperationFilter
{
/// <summary>
/// Applies the filter to the specified operation using the given context.
/// </summary>
/// <param name="operation">The operation to apply the filter to.</param>
/// <param name="context">The current operation filter context.</param>
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
// REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412
// REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413
foreach (var parameter in operation.Parameters)
{
var description = context.ApiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name);
var routeInfo = description.RouteInfo;
if (parameter.Description == null)
{
parameter.Description = description.ModelMetadata?.Description;
}
if (routeInfo == null)
{
continue;
}
// if (parameter.Default == null)
// {
// parameter.Default = routeInfo.DefaultValue;
// }
parameter.Required |= !routeInfo.IsOptional;
}
}
}
// /// <summary>
// /// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter.
// /// </summary>
// /// <remarks>This <see cref="IOperationFilter"/> is only required due to bugs in the <see cref="SwaggerGenerator"/>.
// /// Once they are fixed and published, this class can be removed.</remarks>
// public class SwaggerDefaultValues : IOperationFilter
// {
// /// <summary>
// /// Applies the filter to the specified operation using the given context.
// /// </summary>
// /// <param name="operation">The operation to apply the filter to.</param>
// /// <param name="context">The current operation filter context.</param>
// public void Apply( Operation operation, OperationFilterContext context )
// {
// // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412
// // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413
// foreach ( var parameter in operation.Parameters.OfType<NonBodyParameter>() )
// {
// var description = context.ApiDescription.ParameterDescriptions.First( p => p.Name == parameter.Name );
// var routeInfo = description.RouteInfo;
// if ( parameter.Description == null )
// {
// parameter.Description = description.ModelMetadata?.Description;
// }
// if ( routeInfo == null )
// {
// continue;
// }
// AuthResponsesOperationFilter.cs
public class AuthResponsesOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var authAttributes = context.MethodInfo.DeclaringType.GetCustomAttributes(true)
.Union(context.MethodInfo.GetCustomAttributes(true))
.OfType<AuthorizeAttribute>();
// if ( parameter.Default == null )
// {
// parameter.Default = routeInfo.DefaultValue;
// }
if (authAttributes.Any())
operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
}
}
}
// parameter.Required |= !routeInfo.IsOptional;
// }
// }
// }
// }