Closed as duplicate of#58968
Closed as duplicate of#58968
Description
Is there an existing issue for this?
- I have searched the existing issues
Describe the bug
I was migrating an ASP.NET Core Web API from .NET 8.0
to .NET 9.0
.
As part of the migration, I was replacing AddSwaggerGen()
with AddOpenApi()
and seeing this critical issue. This basically inserts duplicated schema to OpenAPI specification. Solid OpenAPI specification is critical when integrating with services like APIM and hence subject is marked with Critical.
Consider the following minimal reproducible example.
csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="7.2.0" />
</ItemGroup>
</Project>
Startup.cs
namespace WebApplication3;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddOpenApi();
services.AddControllers();
}
public void Configure(IApplicationBuilder app)
{
app.UseSwaggerUI(x =>
{
x.SwaggerEndpoint("/openapi/v1.json", "My API");
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapOpenApi();
endpoints.MapControllers();
});
}
}
Program.cs
namespace WebApplication3;
public class Program
{
public static void Main(string[] args)
{
IHost host = CreateHostBuilder(args).Build();
host.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
WeatherForecastController.cs
using Microsoft.AspNetCore.Mvc;
namespace WebApplication3.Controllers;
[ApiController]
[Route("/api/[controller]")]
public class WeatherForecastController : ControllerBase
{
[HttpGet]
public ActionResult<IEnumerable<WeatherForecast>> Get()
{
return Ok();
}
[HttpPost]
public ActionResult<WeatherForecast> Create([FromBody] WeatherForecast weatherForecast)
{
return weatherForecast;
}
}
Up to here, it's pretty standard.
Now carefully review the following WeatherForecast
type.
namespace WebApplication3;
public class WeatherForecast
{
public int Id { get; set; }
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public LocationDto Location { get; set; }
}
public class LocationDto
{
public string Name { get; set; }
public AddressDto Address { get; set; }
}
public class AddressDto
{
string AddressLine { get; set; }
// A circular reference, but not getting any errors
public LocationDto RelatedLocation { get; set; }
}
Note the highlighted schema.
OpenAPI specification
{
"openapi": "3.0.1",
"info": {
"title": "WebApplication3 | v1",
"version": "1.0.0"
},
"servers": [
{
"url": "https://localhost:7286/"
},
{
"url": "http://localhost:5013/"
}
],
"paths": {
"/api/WeatherForecast": {
"get": {
"tags": [
"WeatherForecast"
],
"responses": {
"200": {
"description": "OK",
"content": {
"text/plain": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/WeatherForecast"
}
}
},
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/WeatherForecast"
}
}
},
"text/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/WeatherForecast"
}
}
}
}
}
}
},
"post": {
"tags": [
"WeatherForecast"
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/WeatherForecast2"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/WeatherForecast2"
}
},
"application/*+json": {
"schema": {
"$ref": "#/components/schemas/WeatherForecast2"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "OK",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/WeatherForecast2"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/WeatherForecast2"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/WeatherForecast2"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"AddressDto": {
"type": "object",
"properties": {
"relatedLocation": {
"$ref": "#/components/schemas/LocationDto2"
}
}
},
"AddressDto2": {
"type": "object",
"properties": {
"relatedLocation": {
"$ref": "#/components/schemas/LocationDto4"
}
}
},
"LocationDto": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"address": {
"$ref": "#/components/schemas/AddressDto"
}
}
},
"LocationDto2": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"address": {
"$ref": "#/components/schemas/#/items/properties/location/properties/address"
}
}
},
"LocationDto3": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"address": {
"$ref": "#/components/schemas/AddressDto2"
}
}
},
"LocationDto4": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"address": {
"$ref": "#/components/schemas/#/properties/location/properties/address"
}
}
},
"WeatherForecast": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int32"
},
"date": {
"type": "string",
"format": "date"
},
"temperatureC": {
"type": "integer",
"format": "int32"
},
"location": {
"$ref": "#/components/schemas/LocationDto"
}
}
},
"WeatherForecast2": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int32"
},
"date": {
"type": "string",
"format": "date"
},
"temperatureC": {
"type": "integer",
"format": "int32"
},
"location": {
"$ref": "#/components/schemas/LocationDto3"
}
}
}
}
},
"tags": [
{
"name": "WeatherForecast"
}
]
}
Expected Behavior
Under components.schemas
, there should be only 3 schemas
- AddressDto
- LocationDto
- WeatherForecast
Steps To Reproduce
Exceptions (if any)
N/A
.NET Version
9.0.200-preview.0.24575.35
Anything else?
N/A