Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update ApiControllerBase.cs #11

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Conversation

Soruk
Copy link

@Soruk Soruk commented Dec 8, 2021

Add Attribute "ApiController" in order to generate Swagger documentation

[ApiController]

Add Attribute  [ApiController] in order to generate Swagger documentation
@Soruk
Copy link
Author

Soruk commented Dec 8, 2021

I think it might be usefull also to add grouping of APIby project

 [ApiExplorerSettings(GroupName = "Name-Group")]

@thomasduft
Copy link
Owner

thomasduft commented Dec 9, 2021

Hi Soruk

Is having the ApiController-attribute a requirement in order to generate Swagger documentation or will it enrich the generated documentation with additional information? Honestly I do not know the details here. I thought it should be already be possible with what is there at the moment.

Regarding the ApiExplorerSettings-attribute, this is already set directly on the specific endpoint-Controllers (see the below picture).

image

And yes, they are hardcoded strings meaning an integrator can not change it to his will. Any suggestions on this?

@Soruk
Copy link
Author

Soruk commented Dec 9, 2021

Hi @thomasduft, the ApiController-attribute is mandatory to generate the Swagger documentation.
When I reference your nugets for the use with Swagger (btw the included XML documentation is not complete) I do not have any endpoints exposed by your API. I had the same issue with my main executing assemble, and when I added the given attribute, the Swagger documentation was generated for given endpoint.

@thomasduft
Copy link
Owner

Hi @Soruk

This is weird?! When I use and reference my latest published nuget openiddict-ui packages and setup the Swagger related stuff within the Startup/Program.cs file then I see the endpoints and I can also browse them.

image

I usually develop within devcontainers on a linux based PC with VSCode. So to me the ApiController-attribute doesn't seem to be mandatory.

@Soruk
Copy link
Author

Soruk commented Dec 9, 2021

Hi @thomasduft

Yes it is weird.
Can you share your Startup/Program.cs, please ?

@thomasduft
Copy link
Owner

thomasduft commented Dec 9, 2021

Et voilà...

using System.Reflection;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.EntityFrameworkCore;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerUI;

using tomware.Suite.Host.Web;
using tomware.Suite.Persistence.EF;

var configuration = new ConfigurationBuilder()
  .AddJsonFile("appsettings.json")
  .Build();

var builder = WebApplication.CreateBuilder(args);

// Add services to the container => ConfigureServices
builder.Services.Configure<StorageContextOptions>(options =>
{
  options.DbContextOptionsBuilder = builder =>
     builder.UseSqlite(configuration.GetConnectionString("DefaultConnection"),
       sql => sql.MigrationsAssembly(typeof(Program)
                 .GetTypeInfo()
                 .Assembly
                 .GetName()
                 .Name));
});

builder.Services.AddModules();

if (builder.Environment.IsDevelopment())
{
  builder.Services.AddSwaggerGen(c =>
  {
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "App", Version = "v1" });
    c.DocInclusionPredicate((name, api) => true);
    c.TagActionsBy(api =>
    {
      if (api.GroupName != null)
      {
        return new[] { api.GroupName };
      }

      var controllerActionDescriptor = api.ActionDescriptor as ControllerActionDescriptor;
      if (controllerActionDescriptor != null)
      {
        return new[] { controllerActionDescriptor.ControllerName };
      }

      throw new InvalidOperationException("Unable to determine tag for endpoint.");
    });
    c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
  });
}

// Configure
var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
  app.UseCors(builder =>
  {
    builder.WithOrigins("http://localhost:4200");
    builder.AllowAnyHeader();
    builder.AllowAnyMethod();
    builder.AllowCredentials();
  });

  app.UseDeveloperExceptionPage();
  app.UseSwagger();
  app.UseSwaggerUI(c =>
  {
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "App v1");
    c.DocExpansion(DocExpansion.None);
  });
}

app.UseWebAppWithDefaults();

app.Run();

It is part of private stuff and already on .NET 6. But you should see the swagger configuration section....

@Soruk
Copy link
Author

Soruk commented Dec 9, 2021

Thanks.

I will check it tomorrow

@Soruk
Copy link
Author

Soruk commented Dec 10, 2021

Ok, I have just tested your code, and still I get only the main / executing assembly api endpoints. I do not know why I cannot to get to show the api exposed by referenced assemblies.

@thomasduft
Copy link
Owner

thomasduft commented Dec 10, 2021

Same as well if you clone this repo and start the sample server? I can run it on a Linux based PC and a Windows based PC and always see the Swagger generated docs!

@Soruk
Copy link
Author

Soruk commented Dec 10, 2021

I have just re-downloaded the code from main branch and I can see the other assemblies documentation.
When I was using the code downloaded just after release of 1.3 version, the swagger didn't work. But it works, strange...
I will be investigating and keep you posted.

@Soruk
Copy link
Author

Soruk commented Dec 10, 2021

Hi @thomasduft, I think that I found the source of the problem.
When I use

services.AddVersionedApiExplorer();

for versioning API, and the Controller does not have the given attribute [ApiController], the Swagger documentation is not generated.

I will check this solution from StackOverflow

@Soruk
Copy link
Author

Soruk commented Dec 10, 2021

I will check this solution from StackOverflow

And it seems that UseApiBehavior = false is the solution.

@Soruk
Copy link
Author

Soruk commented Dec 10, 2021

After more investigation, when I want to filer by version the methods in the following Swagger configration:

   options.DocInclusionPredicate((version, desc) =>
                {
                    if (string.IsNullOrWhiteSpace(desc.GroupName))
                        return false;

                    if (!desc.TryGetMethodInfo(out MethodInfo methodInfo))
                        return false;

                    var versions = methodInfo.DeclaringType
                    .GetCustomAttributes(true)
                    .OfType<ApiVersionAttribute>()
                    .SelectMany(attr => attr.Versions);

                    var maps = methodInfo
                    .GetCustomAttributes(true)
                    .OfType<MapToApiVersionAttribute>()
                    .SelectMany(attr => attr.Versions)
                    .ToArray();

                    return versions.Any(v => $"v{v}" == version)
                    && (!maps.Any() || maps.Any(v => $"v{v}" == version));
                });

without the [ApiVersion], the filter return false.

A little tweak, fix it:

   options.DocInclusionPredicate((version, desc) =>
                {
                    if (string.IsNullOrWhiteSpace(desc.GroupName))
                        return false;

                    if (!desc.TryGetMethodInfo(out MethodInfo methodInfo))
                        return false;

                    var versions = methodInfo.DeclaringType
                    .GetCustomAttributes(true)
                    .OfType<ApiVersionAttribute>()
                    .SelectMany(attr => attr.Versions);

                    var maps = methodInfo
                    .GetCustomAttributes(true)
                    .OfType<MapToApiVersionAttribute>()
                    .SelectMany(attr => attr.Versions)
                    .ToArray();

                       return !versions.Any()  // without version attribute
                        ||
                        (   versions.Any(v => $"v{v}" == version)
                        && (!maps.Any() || maps.Any(v => $"v{v}" == version))
                    );
                });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants