.NET 9 Open API Migration not returning the API Schema
I am migrating a .NET 6 API application to .NET 9.
Installed the Microsoft.AspNetCore.OpenApi and Microsoft.Extensions.ApiDescription.Server packages with version 9.0.2. I am keeping the Swashbuckle.AspNetCore 6.5.0 for the UI experience.
Here is the relevant piece of program.cs
public static class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
var config = GetAppSettingConfigurations();
if (config["AppConfiguration:EnableSerilog"].ToBoolean())
{
builder.Services.AddSerilog(config =>
{
config.ReadFrom.Configuration(builder.Configuration);
});
}
builder.Services.AddOpenApi("internal");
builder.Host.ConfigureLogging();
builder.Host.ConfigureAppConfiguration();
var startup = new Startup(builder.Configuration);
startup.ConfigureServices(builder.Services);
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
startup.Configure(app, app.Environment);
app.Run();
}
private static IHostBuilder ConfigureAppConfiguration(this IHostBuilder host) =>
host.ConfigureAppConfiguration(
(hostingContext, ctxConfig) =>
{
ctxConfig.AddEnvironmentVariables("AppConfiguration");
});
private static IHostBuilder ConfigureLogging(this IHostBuilder host) =>
host.ConfigureLogging(
(hostingContext, logging) =>
{
var instrumentationKey =
hostingContext.Configuration["ApplicationInsights:InstrumentationKey"];
var loggingSection = hostingContext.Configuration.GetSection("Logging");
logging.AddConfiguration(loggingSection);
logging.AddConsole();
logging.AddDebug();
logging.AddEventSourceLogger();
logging.AddApplicationInsights(instrumentationKey);
if (hostingContext.Configuration["ApplicationInsights:EnableSerilog"].ToBoolean())
{
logging.AddSerilog();
}
});
private static IConfigurationRoot GetAppSettingConfigurations() =>
new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", true)
.AddEnvironmentVariables()
.Build();
}
And the program.cs
public class Startup
{
private const string _apiName = "Client API";
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
/// <summary>
/// Configures the application.
/// It is called by the runtime and configures the HTTP request pipeline and is where middleware is setup.
/// </summary>
/// <param name="app">The application builder.</param>
/// <param name="env">The web host environment.</param>
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
if (Configuration.GetSection("AppConfiguration")["EnableSerilog"].ToBoolean())
{
app.UseSerilogRequestLogging();
}
// app.UseSwagger();
app.UseSwaggerUI(
c =>
{
c.SwaggerEndpoint(
"/openapi/v1.json",
_apiName);
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
/// <summary>
/// Configures the services. It is called by the runtime.
/// </summary>
/// <param name="services">The service collection.</param>
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddMvc(options => { options.Filters.Add<UnhandledExceptionFilterAttribute>(); });
services.AddControllersWithViews()
.AddJsonOptions(
options =>
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));
ConfigureAutoMapper(services);
ConfigureDependency(services);
ConfigureFluentValidation(services);
services.AddApplicationInsightsTelemetry();
}
/// <summary>Configures the dependency injection collection.</summary>
/// <param name="services">The services.</param>
private static void ConfigureDependency(IServiceCollection services)
{
// Application Insight / Telemetry
services.AddScoped<IDependencyTelemetryClient, DependencyTelemetryClient>();
//Services
services.AddScoped<IConfigurationService, ConfigurationService>();
services.AddScoped<IClientService, ClientService>();
// Repository
services.AddScoped<IClientRepository, ClientRepository>();
// UCI Layer
services.AddScoped<IUciClientFactory, UciClientFactory>();
services.AddScoped(x => x.GetService<IUciClientFactory>().CreateFactory());
}
/// <summary>
/// Configures the fluent validation so that it will trigger when ModelState.isValid is called.
/// </summary>
/// <remarks>
/// It will execute the correct validator (if found via dependency injection), then execute the data annotation on
/// that model.
/// </remarks>
/// <param name="services">The services.</param>
private static void ConfigureFluentValidation(IServiceCollection services)
{
services.AddMvc()
.AddFluentValidation();
// Dependency Injection using Add Transient instead of Add Scope per documentation. https://fluentvalidation.net/aspnet
}
/// <summary>Configures the automatic mapper system.</summary>
/// <param name="services">The services.</param>
private void ConfigureAutoMapper(IServiceCollection services)
{
var config = new MapperConfiguration(cfg => { cfg.AddProfile(new UciClientMappingProfile()); });
var mapper = config.CreateMapper();
mapper.ConfigurationProvider.AssertConfigurationIsValid();
services.AddSingleton(mapper);
}
}
Here is the controller code too
[ApiController]
public class ClientController : ControllerBase
{
private readonly IClientService _clientService;
private readonly ILogger _logger;
public ClientController(
ILogger<ClientController> logger,
IClientService clientService)
{
_logger = logger;
_clientService = clientService;
}
/// <summary>Retrieve all the details of the client.</summary>
/// <param name="wwId">The world wide unique WwId.</param>
/// <returns>Returns detail client</returns>
[Route("clients/{wwId}")]
[HttpGet]
[ProducesResponseType(typeof(Client), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> GetClient([FromRoute] [Required] int wwId)
{
using var scope = _logger.BeginScope(
"GetClient => ScopeId: {ScopeId}; WwId: {WwId}",
Guid.NewGuid(),
wwId);
_logger.LogInformation("GetClient => Started");
ModelState.CheckModelValidation();
var request = new GetClientRequest
{
WwId = wwId
};
var results = await _clientService.GetClientAsync(request);
return Ok(results);
}
}
Executed the application and trying to get the /openapi/v1.json . It is returning 404 error without any proper exception details from debug. Can someone provide insight on it?