4
votes

I want to use both OData and Swagger in my Web API. I'm running ASP.NET Core 3.1.

I have found these articles, one to enable OData and another to enable SwaggerUI

However, I can't seem to enable both at the same time. It seems that I'm mixing them wrong.

This is the code that I have currently:

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.AddControllers();
        services.AddOData();
        AddSwagger(services);
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();
        app.UseRouting();
        app.UseAuthorization();

        app.UseSwagger();
        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("/swagger/v1/swagger.json", "Foo API V1");
        });

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.Select().Filter().OrderBy().Count().MaxTop(10);
            endpoints.MapODataRoute("odata", "odata", GetEdmModel());
        });
    }

    private IEdmModel GetEdmModel()
    {
        var odataBuilder = new ODataConventionModelBuilder();
        odataBuilder.EntitySet<WeatherForecast>("WeatherForecast");

        return odataBuilder.GetEdmModel();
    }

    private void AddSwagger(IServiceCollection services)
    {
        services.AddSwaggerGen(options =>
        {
            var groupName = "v1";

            options.SwaggerDoc(groupName, new OpenApiInfo
            {
                Title = $"Foo {groupName}",
                Version = groupName,
                Description = "Foo API",
                Contact = new OpenApiContact
                {
                    Name = "Foo Company",
                    Email = string.Empty,
                    Url = new Uri("https://example.com/"),
                }
            });
        });
    }
}

It works when I go to https://localhost:44363/odata/weatherforecast But when I try to load the Swagger interface, this is showing:

enter image description here

It doesn't show anything!

This is my controller:

Controller

[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };
    
    [EnableQuery]
    public IEnumerable<WeatherForecast> Get()
    {
        var rng = new Random();
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Id = Guid.NewGuid(),
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            })
            .ToArray();
    }
}
4
Same issue here. The OData endpoint is registered alright, however it is not showing up in the UI. What is worse, there's no information in the OpenAPI definition json, so it is useless for setting up API management gateway. - sergevm

4 Answers

2
votes

I used below nuget package and this issue got resolved. Install-Package OData.Swagger

Ref: https://github.com/KishorNaik/Sol_OData_Swagger_Support

4
votes

My understanding is that the combination of:

  • ASP.NET Core 3.1
  • Endpoint routing
  • OData (even 7.4+)
  • Swagger

does not really work at this time because there is no good ApiExplorer implementation for OData controllers/routing. However, I had the same issue and I was able to make actions appear in Swagger/UI using this :

[ApiExplorerSettings(IgnoreApi = false)]
[Route("Data")]
[HttpGet]
public async Task<IEnumerable<Data>> GetData()
{
  // ...
}

and by applying this in Startup code (adapted from This) :

services.AddControllers(options =>
{
    IEnumerable<ODataOutputFormatter> outputFormatters =
        options.OutputFormatters.OfType<ODataOutputFormatter>()
            .Where(formatter => !formatter.SupportedMediaTypes.Any());

    foreach (var outputFormatter in outputFormatters)
    {
        outputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/odata"));
    }

    IEnumerable<ODataInputFormatter> inputFormatters =
        options.InputFormatters.OfType<ODataInputFormatter>()
            .Where(formatter => !formatter.SupportedMediaTypes.Any());

    foreach (var inputFormatter in inputFormatters)
    {
        inputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/odata"));
    }
});

However, this works for some actions but I do not think this is a good fix since it forces you to reproduce OData conventions routing with non-OData API routing metadata ([Route] + HTTP verbs attributes) everywhere. This is non-sense !

It would be wonderful to be able to automatically generate an OpenAPI document from the entire API, using EDM and OData conventions...

Resources:

1
votes

Make this change.

c.SwaggerEndpoint("../swagger/v1/swagger.json", "Foo API V1");

Basically it not able to read your swagger.json file.

0
votes
app.UseSwaggerUI(c =>
  {
      c.SwaggerEndpoint("/swagger/v1/swagger.json", "Swagger Demo Project");
  });

For more detail: https://findandsolve.com/articles/how-to-implemenation-swagger-in-asp-net-core