Ok, everything seems to be working except for the api explorer so now can turn back to that. (api explorer issues are caused by the api versioning system as removing it fixes the missing api explorer routes

This commit is contained in:
2019-10-17 18:42:28 +00:00
parent c9155ff24e
commit 0d42f163fc
27 changed files with 285 additions and 375 deletions

View File

@@ -11,29 +11,6 @@ UPDATE all the things before commencing work
- https://github.com/domaindrivendev/Swashbuckle.AspNetCore#swashbuckleaspnetcoreannotations - 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 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. Do the stuff in the Client todo first then back to the server as required.

View File

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

View File

@@ -11,9 +11,9 @@ namespace AyaNova.Api.Controllers
/// <summary> /// <summary>
/// Meta controller class /// Meta controller class
/// </summary> /// </summary>
[ApiVersion("8.0")]
[Route("api/v{version:apiVersion}/")]
[AllowAnonymous] [AllowAnonymous]
//SWASHBUCKLETEST [ApiVersion("8.0")]
[Route("api/v8/")]//was [Route("api/v{version:apiVersion}/")]
public class ApiMetaController : Controller public class ApiMetaController : Controller
{ {
private readonly ApiServerState serverState; private readonly ApiServerState serverState;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -22,8 +22,8 @@ namespace AyaNova.Api.Controllers
/// <summary> /// <summary>
/// Sample controller class used during development for testing purposes /// Sample controller class used during development for testing purposes
/// </summary> /// </summary>
//SWASHBUCKLETEST [ApiVersion("8.0")] [ApiVersion("8.0")]
[Route("api/v8/[controller]")]//was [Route("api/v{version:apiVersion}/[controller]")] [Route("api/v{version:apiVersion}/[controller]")]
[Produces("application/json")] [Produces("application/json")]
[Authorize] [Authorize]
public class WidgetController : Controller 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 .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) .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) .UseIISIntegration()//support IIS integration just in case, it appears here to override prior settings if necessary (port)
// .ConfigureMetricsWithDefaults(builder => .ConfigureMetricsWithDefaults(builder =>
// { {
// if (ServerBootConfig.AYANOVA_METRICS_USE_INFLUXDB) if (ServerBootConfig.AYANOVA_METRICS_USE_INFLUXDB)
// { {
// builder.Report.ToInfluxDb( builder.Report.ToInfluxDb(
// options => options =>
// { {
// options.InfluxDb.BaseUri = new Uri(ServerBootConfig.AYANOVA_METRICS_INFLUXDB_BASEURL); options.InfluxDb.BaseUri = new Uri(ServerBootConfig.AYANOVA_METRICS_INFLUXDB_BASEURL);
// options.InfluxDb.Database = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_DBNAME; options.InfluxDb.Database = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_DBNAME;
// if (!string.IsNullOrWhiteSpace(ServerBootConfig.AYANOVA_METRICS_INFLUXDB_CONSISTENCY)) if (!string.IsNullOrWhiteSpace(ServerBootConfig.AYANOVA_METRICS_INFLUXDB_CONSISTENCY))
// { {
// options.InfluxDb.Consistenency = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_CONSISTENCY; options.InfluxDb.Consistenency = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_CONSISTENCY;
// } }
// options.InfluxDb.UserName = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_USERNAME; options.InfluxDb.UserName = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_USERNAME;
// options.InfluxDb.Password = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_PASSWORD; options.InfluxDb.Password = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_PASSWORD;
// if (!string.IsNullOrWhiteSpace(ServerBootConfig.AYANOVA_METRICS_INFLUXDB_RETENTION_POLICY)) if (!string.IsNullOrWhiteSpace(ServerBootConfig.AYANOVA_METRICS_INFLUXDB_RETENTION_POLICY))
// { {
// options.InfluxDb.RetentionPolicy = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_RETENTION_POLICY; options.InfluxDb.RetentionPolicy = ServerBootConfig.AYANOVA_METRICS_INFLUXDB_RETENTION_POLICY;
// } }
// options.InfluxDb.CreateDataBaseIfNotExists = true; options.InfluxDb.CreateDataBaseIfNotExists = true;
// options.HttpPolicy.BackoffPeriod = TimeSpan.FromSeconds(30); options.HttpPolicy.BackoffPeriod = TimeSpan.FromSeconds(30);
// options.HttpPolicy.FailuresBeforeBackoff = 5; options.HttpPolicy.FailuresBeforeBackoff = 5;
// options.HttpPolicy.Timeout = TimeSpan.FromSeconds(10); options.HttpPolicy.Timeout = TimeSpan.FromSeconds(10);
// //options.MetricsOutputFormatter = new App.Metrics.Formatters.Json.MetricsJsonOutputFormatter(); //options.MetricsOutputFormatter = new App.Metrics.Formatters.Json.MetricsJsonOutputFormatter();
// //options.Filter = filter; //options.Filter = filter;
// options.FlushInterval = TimeSpan.FromSeconds(20); options.FlushInterval = TimeSpan.FromSeconds(20);
// }); });
// } }
// }) })
// .UseMetricsEndpoints(opt => .UseMetricsEndpoints(opt =>
// { {
// opt.EnvironmentInfoEndpointEnabled = false; opt.EnvironmentInfoEndpointEnabled = false;
// opt.MetricsEndpointEnabled = false; opt.MetricsEndpointEnabled = false;
// opt.MetricsTextEndpointEnabled = false; opt.MetricsTextEndpointEnabled = false;
// }) })
// .UseMetrics() .UseMetrics()
.UseStartup<Startup>() .UseStartup<Startup>()
.ConfigureLogging((context, logging) => .ConfigureLogging((context, logging) =>
{ {

View File

@@ -62,31 +62,36 @@ namespace AyaNova
{ {
_log.LogDebug("BOOT: initializing services..."); _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 //Server state service for shutting people out of api
_log.LogDebug("BOOT: init ApiServerState service"); _log.LogDebug("BOOT: init ApiServerState service");
services.AddSingleton(new AyaNova.Api.ControllerHelpers.ApiServerState()); services.AddSingleton(new AyaNova.Api.ControllerHelpers.ApiServerState());
//Init controllers
_log.LogDebug("BOOT: init controllers");
var MvcBuilder = services.AddControllers(config =>
{
config.Filters.Add(new AyaNova.Api.ControllerHelpers.ApiCustomExceptionFilter(AyaNova.Util.ApplicationLogging.LoggerFactory));
});
_log.LogDebug("BOOT: init JSON");
MvcBuilder.AddNewtonsoftJson(options =>
{
options.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
});
//2019-10-17 METRICS will not work just yet with .netcore 3.1 see here https://github.com/AppMetrics/AppMetrics/issues/480
//awaiting a new release from them
//_log.LogDebug("BOOT: init Metrics service");
//MvcBuilder.AddMetrics();
//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();
// add the versioned api explorer, which also adds IApiVersionDescriptionProvider service // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service
// note: the specified format code will format the version as "'v'major[.minor][-status]" // note: the specified format code will format the version as "'v'major[.minor][-status]"
_log.LogDebug("BOOT: init ApiExplorer service"); _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..."); _log.LogDebug("BOOT: ensuring user and backup folders exist and are separate locations...");
FileUtil.EnsureUserAndUtilityFoldersExistAndAreNotIdentical(_hostingEnvironment.ContentRootPath); FileUtil.EnsureUserAndUtilityFoldersExistAndAreNotIdentical(_hostingEnvironment.ContentRootPath);
@@ -143,46 +148,31 @@ namespace AyaNova
#endregion #endregion
_log.LogDebug("BOOT: init ApiVersioning service"); _log.LogDebug("BOOT: init ApiVersioning service");
// services // services.AddApiVersioning(o => o.ReportApiVersions = true);
// .AddApiVersioning(options => services
// { .AddApiVersioning(options =>
// options.AssumeDefaultVersionWhenUnspecified = true; {
// options.DefaultApiVersion = Microsoft.AspNetCore.Mvc.ApiVersion.Parse("8.0"); options.AssumeDefaultVersionWhenUnspecified = true;
// options.ReportApiVersions = true; options.DefaultApiVersion = Microsoft.AspNetCore.Mvc.ApiVersion.Parse("8.0");
// }); options.ReportApiVersions = true;
});
//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
// // Add service and create Policy with options _log.LogDebug("BOOT: init CORS service");
// _log.LogDebug("BOOT: init CORS service"); services.AddCors(options =>
// services.AddCors(options => {
// { options.AddPolicy("CorsPolicy",
// options.AddPolicy("CorsPolicy", builder => builder.AllowAnyOrigin()
// builder => builder.AllowAnyOrigin() .AllowAnyMethod()
// .AllowAnyMethod() .AllowAnyHeader()
// .AllowAnyHeader() //.AllowCredentials()
// //.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));
// }).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 #region Swagger
//https://docs.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger?tabs=visual-studio-code //https://docs.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger?tabs=visual-studio-code
@@ -190,112 +180,73 @@ namespace AyaNova
//https://github.com/domaindrivendev/Swashbuckle.AspNetCore //https://github.com/domaindrivendev/Swashbuckle.AspNetCore
_log.LogDebug("BOOT: init API explorer service"); _log.LogDebug("BOOT: init API explorer service");
// services.AddSwaggerGen(c => services.AddSwaggerGen(
// { c =>
// c.SwaggerDoc("v8", new OpenApiInfo { Title = "My API", Version = "v8" }); {
// c.OperationFilter<AuthResponsesOperationFilter>(); // 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
// services.AddSwaggerGen( // note: you might choose to skip or document deprecated API versions differently
// c => foreach (var description in provider.ApiVersionDescriptions)
// { {
c.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
}
// 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>();
// c.DocInclusionPredicate((docName, apiDesc) => // integrate xml comments
// { c.IncludeXmlComments(XmlCommentsFilePath);
// 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);
// });
// // resolve the IApiVersionDescriptionProvider service //2019-10-15 - Removed this because apikeyscheme is no longer recognized and it appears it *may* not be necessary... TWT
// // note: that we have to build a temporary service provider here because one has not been created yet //If I have any issues with bearer tokens in swagger then this is probably necessary but in a new way
// var provider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>(); //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"
// // 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 //Obsolete way
// // c.AddSecurityRequirement(new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>> // c.AddSecurityRequirement(new System.Collections.Generic.Dictionary<string, System.Collections.Generic.IEnumerable<string>>
// // { // {
// // { "Bearer", new string[] { } } // { "Bearer", new string[] { } }
// // }); // });
// //https://stackoverflow.com/questions/56234504/migrating-to-swashbuckle-aspnetcore-version-5 //https://stackoverflow.com/questions/56234504/migrating-to-swashbuckle-aspnetcore-version-5
// //First we define the security scheme //First we define the security scheme
// c.AddSecurityDefinition("Bearer", //Name the security scheme c.AddSecurityDefinition("Bearer", //Name the security scheme
// new OpenApiSecurityScheme new OpenApiSecurityScheme
// { {
// Description = "JWT Authorization header using the Bearer scheme.", Description = "JWT Authorization header using the Bearer scheme.",
// Type = SecuritySchemeType.Http, //We set the scheme type to http since we're using bearer authentication 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". Scheme = "bearer" //The name of the HTTP Authorization scheme to be used in the Authorization header. In this case "bearer".
// }); });
// c.AddSecurityRequirement(new OpenApiSecurityRequirement{ c.AddSecurityRequirement(new OpenApiSecurityRequirement{
// { {
// new OpenApiSecurityScheme{ new OpenApiSecurityScheme{
// Reference = new OpenApiReference{ Reference = new OpenApiReference{
// Id = "Bearer", //The name of the previously defined security scheme. Id = "Bearer", //The name of the previously defined security scheme.
// Type = ReferenceType.SecurityScheme Type = ReferenceType.SecurityScheme
// } }
// },new List<string>() },new List<string>()
// } }
// }); });
// //https://github.com/domaindrivendev/Swashbuckle.AspNetCore });
// //ARGGHHHHHHHH!!!!!
// });
#endregion #endregion
@@ -367,20 +318,14 @@ namespace AyaNova
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // 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, public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IWebHostEnvironment env,
AyContext dbContext, AyaNova.Api.ControllerHelpers.ApiServerState apiServerState, IServiceProvider serviceProvider) AyContext dbContext, IApiVersionDescriptionProvider provider, AyaNova.Api.ControllerHelpers.ApiServerState apiServerState, IServiceProvider serviceProvider)
{ {
//This was in constructor right after dbcontext: IApiVersionDescriptionProvider provider,
_log.LogDebug("BOOT: configuring request pipeline..."); _log.LogDebug("BOOT: configuring request pipeline...");
//dotnet3 test added
if (env.IsDevelopment()) if (env.IsDevelopment())
{ {
app.UseDeveloperExceptionPage(); 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 //Store a reference to the dependency injection service for static classes
ServiceProviderProvider.Provider = app.ApplicationServices; ServiceProviderProvider.Provider = app.ApplicationServices;
@@ -394,15 +339,26 @@ namespace AyaNova
_log.LogDebug("BOOT: pipeline - static files"); _log.LogDebug("BOOT: pipeline - static files");
app.UseDefaultFiles(); app.UseDefaultFiles();
app.UseStaticFiles(); 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 #endregion
_log.LogDebug("BOOT: pipeline - ROUTING"); _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 app.UseRouting();//this wasn't here for 2.2 but added for 3.0, needs to come before the stuff after
//v3test _log.LogDebug("BOOT: pipeline - CORS");
// _log.LogDebug("BOOT: pipeline - CORS"); app.UseCors("CorsPolicy");
// app.UseCors("CorsPolicy");
#region AUTH / ROLES #region AUTH / ROLES
@@ -413,6 +369,7 @@ namespace AyaNova
_log.LogDebug("BOOT: pipeline - authorization"); _log.LogDebug("BOOT: pipeline - authorization");
app.UseAuthorization(); app.UseAuthorization();
//Custom middleware to get user roles and put them into the request so //Custom middleware to get user roles and put them into the request so
//they can be authorized in routes. //they can be authorized in routes.
app.Use(async (context, next) => app.Use(async (context, next) =>
@@ -444,46 +401,33 @@ namespace AyaNova
#endregion #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"); _log.LogDebug("BOOT: pipeline - ENDPOINTS");
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>
{ {
endpoints.MapControllers(); endpoints.MapControllers();
}); });
#region SWAGGER #region SWAGGER
_log.LogDebug("BOOT: pipeline - api explorer"); _log.LogDebug("BOOT: pipeline - api explorer");
// // Enable middleware to serve generated Swagger as a JSON endpoint. // Enable middleware to serve generated Swagger as a JSON endpoint.
// app.UseSwagger(); 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 => app.UseSwaggerUI(c =>
{ {
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); // build a swagger endpoint for each discovered API version
c.DefaultModelsExpandDepth(-1); 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.DocumentTitle = "AyaNova API explorer";
c.RoutePrefix = "api-docs"; c.RoutePrefix = "api-docs";
}); });
@@ -494,6 +438,15 @@ 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 ***************************** // ******************** TESTING WIPE DB *****************************
@@ -582,33 +535,33 @@ namespace AyaNova
#region Swagger and API Versioning utilities #region Swagger and API Versioning utilities
// static string XmlCommentsFilePath static string XmlCommentsFilePath
// { {
// get get
// { {
// //Obsolete, used new method: https://developers.de/blogs/holger_vetter/archive/2017/06/30/swagger-includexmlcomments-platformservices-obsolete-replacement.aspx //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 = PlatformServices.Default.Application.ApplicationBasePath;
// var basePath = AppContext.BaseDirectory; var basePath = AppContext.BaseDirectory;
// var fileName = typeof(Startup).GetTypeInfo().Assembly.GetName().Name + ".xml"; var fileName = typeof(Startup).GetTypeInfo().Assembly.GetName().Name + ".xml";
// return Path.Combine(basePath, fileName); return Path.Combine(basePath, fileName);
// } }
// } }
// static Microsoft.OpenApi.Models.OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) static Microsoft.OpenApi.Models.OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
// { {
// var info = new Microsoft.OpenApi.Models.OpenApiInfo() var info = new Microsoft.OpenApi.Models.OpenApiInfo()
// { {
// Title = $"AyaNova API {description.ApiVersion}", Title = $"AyaNova API {description.ApiVersion}",
// Version = description.ApiVersion.ToString() Version = description.ApiVersion.ToString()
// }; };
// if (description.IsDeprecated) if (description.IsDeprecated)
// { {
// info.Description += " This API version has been deprecated."; info.Description += " This API version has been deprecated.";
// } }
// return info; return info;
// } }
#endregion #endregion

View File

@@ -1,69 +1,47 @@
namespace AyaNova // namespace AyaNova
{ // {
using Microsoft.OpenApi.Models; // using Swashbuckle.AspNetCore.SwaggerGen;
using Swashbuckle.AspNetCore.SwaggerGen; // using System.Linq;
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;
// }
// AuthResponsesOperationFilter.cs // if ( routeInfo == null )
public class AuthResponsesOperationFilter : IOperationFilter // {
{ // continue;
public void Apply(OpenApiOperation operation, OperationFilterContext context) // }
{
var authAttributes = context.MethodInfo.DeclaringType.GetCustomAttributes(true)
.Union(context.MethodInfo.GetCustomAttributes(true))
.OfType<AuthorizeAttribute>();
if (authAttributes.Any()) // if ( parameter.Default == null )
operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" }); // {
} // parameter.Default = routeInfo.DefaultValue;
} // }
}
// parameter.Required |= !routeInfo.IsOptional;
// }
// }
// }
// }