Search code examples
c#asp.net-coregenericsswagger-uiswashbuckle.aspnetcore

Swagger "No operations defined in spec!" Generic Base Controller Issue


I have an interface, IItemController<T>, applied to a base ItemController<T> which also inherits ControllerBase.

public class ItemController<T> : ControllerBase, IItemController<T> where T : class {
    private readonly IManager<T> _manager;
    private readonly ILogger<ItemController<T>> _logger;

    public ItemController(ILogger<ItemController<T>> logger, IManager<T> manager)
    {
        _logger = logger;
        _manager = manager;
    }

    [HttpGet($@"test")]
    public IActionResult Test(){
        return NoContent();
    }

    /* Other Methods */
}

I then have a ProductController<T> that inherits the ItemController<T> specifying the Product type:

[ApiController]
[Route("[controller]")]
public class ProductController<T> : ItemController<T> where T : Product
{
    private readonly IManager<T> _manager;
    private readonly ILogger<ProductController<T>> _logger;

    public ProductController(ILogger<ProductController<T>> logger, IManager<T> manager) : base(logger, manager)
    {
        _logger = logger;
        _manager = manager;
    }
}

I would assume this should just generate the swagger for each controller I inherit the ItemController on but I'm getting:

"No operations defined in spec!"

I try to navigate directly to the Urls and they don't appear to be working either so I might have a non-swagger issue.

This is my Program.cs

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();

// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddEntityFrameworkSqlite().AddDbContext<DataContext>(opt =>
    opt.UseSqlite(builder.Configuration.GetConnectionString("SqliteConnection")));

builder.Services.AddTransient(typeof(IManager<>), typeof(Manager<>));
builder.Services.AddTransient(typeof(IPager<>), typeof(Pager<>));
builder.Services.AddTransient(typeof(IRepository<>), typeof(Repository<>));
builder.Services.AddTransient(typeof(IItemController<>), typeof(ItemController<>));

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.MapControllers();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints => endpoints.MapControllers());

app.Run();

Solution

  • I actually simplified it a little bit. I kept ItemController and Program.cs the same and changed the ProductController class like so:

    [ApiController]
    [Route("[controller]")]
    public class ProductController : ItemController<Product>
    {
        public ProductController(ILogger<ProductController> logger, IManager<Product> manager) : base(logger, manager)
        {
        }
    }
    

    I'm not sure WHY the other way didn't work but this is more succinct anyways.