Unable to create swagger.json file when using aspnet-api-versioning - asp.net-core

I have .NET Core 2.2 application. I am trying to set up API with different versions using Microsoft.AspnetCore.Mvc.Versioning nugetpackage. I followed the samples provided in the repository.
I want to use an API version based on the name of the defining controller's namespace.
Project Structure
Controllers
namespace NetCoreApiVersioning.V1.Controllers
{
[ApiController]
[Route("[controller]")]
[Route("v{version:apiVersion}/[controller]")]
public class HelloWorldController : ControllerBase
{
public IActionResult Get()
{
return Ok();
}
}
}
namespace NetCoreApiVersioning.V2.Controllers
{
[ApiController]
[Route("[controller]")]
[Route("v{version:apiVersion}/[controller]")]
public class HelloWorldController : ControllerBase
{
public IActionResult Get()
{
return Ok();
}
}
}
Note the controllers does not have [ApiVersion] attribute becuase i want the versioning to be defined by the namespace
Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddApiVersioning(
options =>
{
// reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions"
options.ReportApiVersions = true;
// automatically applies an api version based on the name of the defining controller's namespace
options.Conventions.Add(new VersionByNamespaceConvention());
});
services.AddVersionedApiExplorer(
options =>
{
// add the versioned api explorer, which also adds IApiVersionDescriptionProvider service
// note: the specified format code will format the version as "'v'major[.minor][-status]"
options.GroupNameFormat = "'v'VVV";
// note: this option is only necessary when versioning by url segment. the SubstitutionFormat
// can also be used to control the format of the API version in route templates
options.SubstituteApiVersionInUrl = true;
});
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "API v1 ", Version = "v1" });
c.SwaggerDoc("v2", new Info { Title = "API v2", Version = "v2" });
});
// commented code below is from
// https://github.com/microsoft/aspnet-api-versioning/tree/master/samples/aspnetcore/SwaggerSample
//services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
//services.AddSwaggerGen(
// options =>
// {
// // add a custom operation filter which sets default values
// //options.OperationFilter<SwaggerDefaultValues>();
// // integrate xml comments
// //options.IncludeXmlComments(XmlCommentsFilePath);
// });
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApiVersionDescriptionProvider provider)
{
// remaining configuration omitted for brevity
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
app.UseSwaggerUI(
options =>
{
// build a swagger endpoint for each discovered API version
foreach (var description in provider.ApiVersionDescriptions)
{
options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
}
});
app.UseMvc();
}
}
Issue
It is not able to generate swagger.json file. When i browse url /swaggger i see error undefined /swagger/v1/swagger.json

found..
i am missing [HttpGet] attribute in ActionMethods

Related

ASP.NET Core 6 OData Swagger UI always shows $count query

I created a new ASP.NET Core Web API project with Swagger and added an ODataController. The Swagger UI shows my Users route as expected, and it works. But the Swagger UI also unexpectedly shows Users/$count:
Why is $count there? Can I prevent it from appearing?
I'm using Microsoft.AspNetCore.OData 8.0.10 (the latest). I get the same results with Swashbuckle.AspNetCore 6.2.3 (the template default) and 6.3.1 (the latest).
My entire controller:
public class UsersController : ODataController
{
private readonly ErpContext _db;
public UsersController(ErpContext db)
{
_db = db;
}
[HttpGet]
public IActionResult Get()
{
return Ok(_db.Users);
}
}
My entire EDM:
public static class EntityDataModel
{
public static IEdmModel Build()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<User>("Users");
return builder.GetEdmModel();
}
}
My entire startup, in case there's some order sensitivity or something:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContextFactory<ErpContext>(options =>
{
options.UseSqlServer(builder.Configuration["ConnectionStrings:DefaultConnection"]);
});
builder.Services
.AddControllers()
.AddOData(options =>
{
options.AddRouteComponents("odata", EntityDataModel.Build());
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Note that my controller action does not have [EnableQuery] and my ODataOptions does not have Count() or any other query. The unwanted $count query doesn't even work unless I add those.
I've reproduced the problem in a minimal project with an in-memory DB.

Swagger is not generating api documentation correctly

I have an asp.net core web api project and I am using version 3.1. I installed the swagger package and configured it, everything is normal, but the API of the swagger page is not seen, why is this, this is the configuration information of my reference document.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoList"));
services.AddControllers();
// Register the Swagger generator, defining 1 or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Version = "v1",
Title = "ToDo API",
Description = "A simple example ASP.NET Core Web API",
TermsOfService = new Uri("https://example.com/terms"),
Contact = new OpenApiContact
{
Name = "Shayne Boyer",
Email = string.Empty,
Url = new Uri("https://twitter.com/spboyer"),
},
License = new OpenApiLicense
{
Name = "Use under LICX",
Url = new Uri("https://example.com/license"),
}
});
// Set the comments path for the Swagger JSON and UI.
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
}
public void Configure(IApplicationBuilder app)
{
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger(c =>
{
c.SerializeAsV2 = true;
});
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
I reproduced the problem according to your code. I created a Controller myself, but since the routing address was not added, the first time I opened it was like this:
There is only one default route, but in my project I have added a HomeController which also doesn't show up in that API.
public class HomeController : Controller
{
[HttpGet]
public string Index()
{
return "test";
}
}
This is because I did not add the routing address and it did not display.
[Route("api/[controller]")]
public class HomeController : Controller
{
[HttpGet]
[Route("test")]
public string Index()
{
return "test";
}
}
After adding:
I don't know if you are for this reason, if not, can you explain your steps in detail? Or provide an interface where your swagger does not display the API.
Reference documentation:
Get started with Swashbuckle and ASP.NET Core

How to implement api versioning and swagger document dynamically

I am working in dotnet core api. I have to implement versioning on api. and swagger document should be categorized by api version.
In .NetCore api versioning can be implement by adding below reference from nuget
Microsoft.AspNetCore.Mvc.Versioning
Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer
After adding reference do following in startup file of your project. Add below line before AddMvc line. I will use Header-api versioning. It means client will mention the version in header. Header name is customizable.
services.AddApiVersioning(this.Configuration);
Definition of AddApiVersioning would be like as (In different extension class):
public static void AddApiVersioning(this IServiceCollection services, IConfiguration configuration)
{
services.AddApiVersioning(apiVersioningOptions =>
{
apiVersioningOptions.ApiVersionReader = new HeaderApiVersionReader(new string[] { "api-version" }); // It means version will be define in header.and header name would be "api-version".
apiVersioningOptions.AssumeDefaultVersionWhenUnspecified = true;
var apiVersion = new Version(Convert.ToString(configuration["DefaultApiVersion"]));
apiVersioningOptions.DefaultApiVersion = new ApiVersion(apiVersion.Major, apiVersion.Minor);
apiVersioningOptions.ReportApiVersions = true;
apiVersioningOptions.UseApiBehavior = true; // It means include only api controller not mvc controller.
apiVersioningOptions.Conventions.Controller<AppController>().HasApiVersion(apiVersioningOptions.DefaultApiVersion);
apiVersioningOptions.Conventions.Controller<UserController>().HasApiVersion(apiVersioningOptions.DefaultApiVersion);
apiVersioningOptions.ApiVersionSelector = new CurrentImplementationApiVersionSelector(apiVersioningOptions);
});
services.AddVersionedApiExplorer(); // It will be used to explorer api versioning and add custom text box in swagger to take version number.
}
Here configuration["DefaultApiVersion"] is a key in appsetting having value 1.0
As in above code we have used Convention to define api version for each controller. It is useful when there is one api version and you don't want to label each controller with [ApiVersion] attribute.
If you don't want to use the Convention menthod to define version of controller. use attribute label to define version. like as below:
[Route("[controller]")]
[ApiController]
[ApiVersion("1.0")]
public class TenantController : ConfigController
Once this done go to StartUp file and add below code.
app.UseApiVersioning(); //Here app is IApplicationBuilder
That is complete solution for api versioning.
For swagger We have to add nuget package as defined below:
Swashbuckle.AspNetCore
Swashbuckle.AspNetCore.SwaggerGen
Swashbuckle.AspNetCore.SwaggerUI
After adding reference do below: Add below line after Services.UseApiVersioning()
services.AddSwaggerGenerationUI();
The definition of AddSwaggerGenerationUI is below in extens :
public static void AddSwaggerGenerationUI(this IServiceCollection services)
{
var provider = services.BuildServiceProvider()
.GetRequiredService<IApiVersionDescriptionProvider>();
services.AddSwaggerGen(action =>
{
action.OrderActionsBy(orderBy => orderBy.HttpMethod);
action.UseReferencedDefinitionsForEnums();
foreach (var item in provider.ApiVersionDescriptions)
{
action.SwaggerDoc(item.GroupName, new Swashbuckle.AspNetCore.Swagger.Info
{
Title = "Version-" + item.GroupName,
Version = item.ApiVersion.MajorVersion.ToString() + "." + item.ApiVersion.MinorVersion
});
}
});
}
This code will add swagger in pipeline. Now we have to use swagger. do below code in startup file.:
app.UseSwaggerGenerationUI(this.Configuration)
Definition of UseSwaggerGenerationUI would be like as :
public static void UseSwaggerGenerationUI(this IApplicationBuilder applicationBuilder, IApiVersionDescriptionProvider apiVersionDescriptionProvider, IConfiguration configuration)
{
applicationBuilder.UseSwagger(c =>
{
c.RouteTemplate = "/api/help/versions/{documentname}/document.json";
c.PreSerializeFilters.Add((swaggerDoc, httpReq) => swaggerDoc.BasePath = "/api");
});
applicationBuilder.UseSwaggerUI(c =>
{
c.RoutePrefix = "api/help";
c.DocumentTitle = "Api Help";
foreach (var item in apiVersionDescriptionProvider.ApiVersionDescriptions)
{
c.SwaggerEndpoint($"/api/help/versions/{item.GroupName}/document.json", item.GroupName);
}
});
}

swagger : Failed to load API definition undefined /swagger/v1/swagger.json

I have tried to configure swagger in my asp.net core api and getting the following error. Failed to load API definition undefined /swagger/v1/swagger.json
I am not sure why I am getting this error. I have added the necessary configuration in the startup file
I have tried the following paths but there has been no difference
/swagger/v1/swagger.json
../swagger/v1/swagger.json
v1/swagger.json
startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSwaggerGen(c =>
{
});
services.AddDbContext<NorthwindContext>(item => item.UseSqlServer(Configuration.GetConnectionString("NorthwindDBConnection")));
services.AddCors(option => option.AddPolicy("MyPolicy", builder => {
builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
}));
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new MappingProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
services.AddScoped<ICustomerRepository, CustomerRepository>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseCors("MyPolicy");
app.UseHttpsRedirection();
app.UseSwagger();
app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "API name"); });
app.UseMvc();
}
}
CustomerController
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Customer.Repository;
using CustomerService.Models;
using CustomerService.ViewModel;
using Microsoft.AspNetCore.Mvc;
namespace CustomerService.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class CustomersController : Controller
{
ICustomerRepository _customersRepository;
public CustomersController(ICustomerRepository customersRepository)
{
_customersRepository = customersRepository;
}
[HttpGet]
[Route("GetCustomers")]
//[NoCache]
[ProducesResponseType(typeof(List<CustomerViewModel>), 200)]
[ProducesResponseType(typeof(ApiResponse), 400)]
public async Task<IActionResult> Customers()
{
try
{
var customers = await _customersRepository.GetAllCustomers();
if (customers == null)
{
return NotFound();
}
return Ok(customers);
}
catch
{
return BadRequest();
}
}
[HttpGet]
[Route("GetCustomer")]
//[NoCache]
[ProducesResponseType(typeof(List<CustomerViewModel>), 200)]
[ProducesResponseType(typeof(ApiResponse), 400)]
public async Task<IActionResult> Customers(string customerId)
{
if (customerId == null)
{
return BadRequest();
}
try
{
var customer = await _customersRepository.GetCustomer(customerId);
if (customer == null)
{
return NotFound();
}
return Ok(customer);
}
catch
{
return BadRequest();
}
}
[HttpPost]
[Route("AddCustomer")]
public async Task<IActionResult> AddCustomer([FromBody] CustomerViewModel model)
{
if (ModelState.IsValid)
{
try
{
var customerId = await _customersRepository.Add(model);
if (customerId != null)
{
return Ok(customerId);
}
else
{
return NotFound();
}
}
catch(Exception ex)
{
return BadRequest();
}
}
return BadRequest();
}
[HttpPost]
[Route("DeleteCustomer")]
public async Task<IActionResult> DeleteCustomer(string customerId)
{
int result = 0;
if (customerId == null)
{
return BadRequest();
}
try
{
var customer = await _customersRepository.Delete(customerId);
if (customer == null)
{
return NotFound();
}
return Ok(customer);
}
catch
{
return BadRequest();
}
}
[HttpPost]
[Route("UpdateCustomer")]
public async Task<IActionResult> UpdateCustomer([FromBody] CustomerViewModel model)
{
if (ModelState.IsValid)
{
try
{
await _customersRepository.Update(model);
return Ok();
}
catch(Exception ex)
{
if (ex.GetType().FullName == "Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException")
{
return NotFound();
}
return BadRequest();
}
}
return BadRequest();
}
}
}
Swagger also cannot deal with two Classes having the same name (at least, not out of the box). So if you have two name spaces, and two classes having the same name, it will fail to initialize.
If you are on the broken Swashbuckle page, Open Dev Tools ... look at the 500 response that Swagger sends back and you will get some great insight.
Here's the dumb thing I was doing ... had a route in the HTTPGet as well as a ROUTE route.
[HttpGet("id")]
[ProducesResponseType(typeof(string), 200)]
[ProducesResponseType(500)]
[Route("{employeeID:int}")]
You are getting an error. Because of you doubled your action names. Look at this example.
Swagger – Failed To Load API Definition , Change [Route("GetCustomers")] names and try again.
I know this was is resolved, but I had this same problem today.
In my case, the problem was that I had a base controller class I created to other controllers inherit from.
The problem started to happen when I created a public function on the base class. Turning it to protected did the trick
This is usually indicative of controllers/actions that Swashbuckle doesn't support for one reason or another.
It's expected that you don't have a swagger.json file in your project. Swashbuckle creates and serves that dynamically using ASP.NET Core's ApiExplorer APIs. What's probably happening here is that Swashbuckle is unable to generate Swagger.json and, therefore, the UI is failing to display.
It's hard to know exactly what caused the failure, so the best way to debug is probably just to remove half your controllers (just move the files to a temporary location) and check whether the issues persists. Then you'll know which half of your controllers contains the troublesome action. You can 'binary search' removing controllers (and then actions) until you figure out which action method is causing Swashbuckle to not be able to generate Swagger.json. Once you know that, it should be obvious whether this is some issue in your code or an issue that should be filed in the Swashbuckle repo.
You could press F12 to open the chrome browser's developer tools to check the cause of failure ,then enter the failed request path and click on the error file to preview the detailed error .
It could also be an issue with ambiguous routes or something like that tripping Swashbuckle up. Once you've narrowed down the cause of failure to something more specific like that, it can either be fixed or filed, as appropriate.
If you want to access swagger via host:port/swagger/v1/swagger.json then you should add options: SwaggerGenOptions inside
public void ConfigureServices(IServiceCollection services)
{
services.AddSwaggerGen(c =>
c.SwaggerDoc("swagger/v1", new OpenApiInfo { Version = "1.0", Title = "API" });
);
}
It should work properly.

I am using NSwag with an ASP.Net Core API and the Swagger UI client is displaying

I am using NSwag with an ASP.Net Core API, when I execute the web API and navigates to the Swagger UI it displays the following error:
Fetching resource list: undefined. Please wait. It gives an 404 and tells me that Cannot read property 'substring' of undefined, that when I tried to trace the error is pointing to the Swagger client in self.url.substring. Although the json displayed in the swagger.json is totally correct.
This is my Startup.cs class with the Explorer Solution at the right showing my nuget dependencies:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
// Enable the Swagger UI middleware and the Swagger generator
app.UseSwaggerUi(typeof(Startup).GetTypeInfo().Assembly, settings =>
{
settings.SwaggerUiRoute = "/swagger";
settings.PostProcess = document =>
{
document.Info.Version = "v1";
document.Info.Title = "Analisis API";
document.Info.Description = "A simple ASP.NET Core web API";
document.Info.TermsOfService = "None";
document.Info.Contact = new NSwag.SwaggerContact
{
Name = "Example",
Email = "example#gmail.com",
Url = "http://google.es"
};
document.Info.License = new NSwag.SwaggerLicense
{
Name = "Use under LICX",
Url = "https://example.com/license"
};
};
app.UseMvc();
});
}
And this is my controller:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : Controller
{
public IDatosAnalisis datosManager = new DatosAnalisis();
public IResultado resultadoManager = new Resultado();
[HttpGet]
public ActionResult<String> GetDefault()
{
return "Bienvenido a AnalisisApi";
}
[HttpGet("getResultado/{patologiaId}")]
[ProducesResponseType(typeof(ResultadoDTO), 200)]
public ActionResult<ResultadoDTO> GetResultadoByPatologiaId(int patologiaId)
{
ResultadoDTO result = resultadoManager.getResultadoByPatologia(patologiaId);
return result;
}
/// <summary>
/// Receives the analisis data and evaluates them.
/// </summary>
[HttpPost]
public ActionResult<List<ShortResultDTO>> TestValoresAnalisis(DatosSujetoDTO datosSujeto)
{
List<ShortResultDTO> results = datosManager.postDatosAnalisisAndGetPatologias(datosSujeto);
return results;
}
}
Thanks in advance for any help given!
Same problem here, my workaround: using a custom url to visit Swagger
https://localhost:44336/swagger/index.html?url=/swagger/v1/swagger.json