1

I am working on a project which I named as "main-project". I have at least 10 different small project in there and this project will probably grow. I have just one swagger for all these projects because all these projects use just one dotnet core web application for generalization purpose.

For example, let's say I have project named SchoolJob and project named HospitalJob. SchoolJob has three different endpoints named GetStudents, TakeStudents, GetPayment and HostpitalJob has two endpoint named GetDoctors and GetNurses. These two different project will be seen in one swagger UI like that:

 - ../schooljob/getstudents
 - ../schooljob/takestudents 
 - ../schooljob/getpayment
 - ../hospitaljob/getdoctors
 - ../hospitaljob/getnurses

What I want to do is setting multiple different swagger page in one dotnet core web project or grouping one swagger so that they will be seen on different page / or on the same page but not the same time.

../mainproject/swagger1/index.html or mainproject/swagger/schooljob UI should be like that:

 - ../schooljob/getstudents
 - ../schooljob/takestudents 
 - ../schooljob/getpayment

../mainproject/swagger2/index.html or mainproject/swagger/hospitaljob UI should be like that

 - ../hospitaljob/getdoctors
 - ../hospitaljob/getnurses
// Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    c.SwaggerDoc("schooljob", new OpenApiInfo
    {
        Version = "schooljob",
        Title = "School Job"
    });
    c.SwaggerDoc("hospitaljob", new OpenApiInfo
    {
        Version = "hospitaljob",
        Title = "Hospital Job"
    });
    // c.AddSecurityDefinition... and other swagger configurations
}   
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider)
{
    app.UseSwagger(c =>
    {
        c.RouteTemplate = "mainproject/swagger/{documentname}/swagger.json";
    });
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/mainproject/swagger/schooljob/swagger.json", "School Job");
        c.SwaggerEndpoint("/mainproject/swagger/hospitaljob/swagger.json", "Hospital Job");
        c.RoutePrefix = "mainproject/swagger";
        /c second prefix how?
    });
}
// SchoolController.cs

[HttpPost("schooljob/getstudents")]
[ApiExplorerSettings(GroupName = "schooljob")]
public JsonResult GetStudents([FromBody]OnaySayfasiId onaySayfasi)
{ ... }
// HospitalController.cs

[HttpPost("hospitaljob/getdoctors")]
[ApiExplorerSettings(GroupName = "hospitaljob")]
public JsonResult GetDoctors([FromBody]OnaySayfasiId onaySayfasi)
{ ... }

By the way, I tried using grouping by Api Version but I saw all these endpoints again. Is there any method for doing this?

Bruno Martins
  • 882
  • 2
  • 10
  • 21
KaraKaplanKhan
  • 734
  • 1
  • 14
  • 31
  • I think using the API versioning should do the job, what did you try exactly? – Bruno Martins Aug 28 '20 at 12:10
  • Also did you try to call `app.UseSwaggerUI(options => { options.RoutePrefix = "swaggerXX" ... })` multiple times in `Startup.cs`? Each call creating a different Swagger page? – Bruno Martins Aug 28 '20 at 12:21
  • How should I do it? Like `mainproject/swagger/schooljob` and `mainproject/swagger/hospitaljob`? Tried it with multiple multiple `SwaggerEndpoint` but it didn't work. – KaraKaplanKhan Aug 28 '20 at 13:09
  • There is just "Select a definition" list on the top-right of the UI and I can select versions. But these two versions have the same endpoint. I have `[ApiExplorerSettings(GroupName = "projectName")]` in only two endpoints and other endpoints don't have this. – KaraKaplanKhan Aug 28 '20 at 13:13
  • Could you add more code to your question, `Startup.cs` and some of yours controllers? It would help a lot to help debug – Bruno Martins Aug 28 '20 at 13:54
  • I couldn't reach the code at weekend, so here my example code. – KaraKaplanKhan Aug 31 '20 at 05:57

2 Answers2

2

I see what you're missing, you used the wrong controller attribute, here what you can do:

  • In the controllers, you have to use [ApiVersion] instead of the [ApiExplorerSettings(GroupName = ...)] attribute. Groups are different, I guess they are about grouping endpoints under the same section in the Swagger page.
  • When a method argument requires a version, be consistent. OpenApiInfo, SwaggerEndpoint, ApiVersion need the same string, for example schooljob and not School Job.

I guess it should work if you update your code as below:

// Startup.cs

app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/mainproject/swagger/schooljob/swagger.json", "schooljob");
        c.SwaggerEndpoint("/mainproject/swagger/hospitaljob/swagger.json", "hospitaljob");
        c.RoutePrefix = "mainproject/swagger";
        // No need for 2nd route prefix as there is only one Swagger page,
        // the content of the page gets updated when selecting a different version in the combobox.
    });

/* This line may be required in ConfigureServices method, give it a try */
/* services.AddApiVersioning(); */
// SchoolController.cs

[HttpPost("schooljob/getstudents")]
[ApiVersion("schooljob")]
public JsonResult GetStudents([FromBody]OnaySayfasiId onaySayfasi)
{ ... }

I hope it solves your problem!

Bruno Martins
  • 882
  • 2
  • 10
  • 21
  • When I used `ApiVersion` I got 'Fetch errorInternal Server Error /bilgemuygulamalari/swagger/toplantiodasi/swagger.json'. What could be the problem? – KaraKaplanKhan Aug 31 '20 at 08:07
  • You can debug it as explained in this [post](https://stackoverflow.com/questions/35788911/500-error-when-setting-up-swagger-in-asp-net-core-mvc-6-app) – Bruno Martins Aug 31 '20 at 08:17
  • Unfortunately, I can't debug errors on Output, browser console or browser network. For example response of `/mainproject/swagger/hospitaljob/swagger.json` returns empty. – KaraKaplanKhan Aug 31 '20 at 11:42
  • I am trying to debug swagger but there is no error message on Chrome, Visual Studio or Postman or any other browser etc. My json is always empty – KaraKaplanKhan Sep 04 '20 at 06:26
  • Oh I forgot something, the thing is your API doesn't know where to pick the version. You must use a `{version:apiVersion}` in your routes for that. For example, try `[HttpPost("{version:apiVersion}/getstudents")]` on top of your method, or try `[Route("{version:apiVersion}")]` on top of your controller. – Bruno Martins Sep 04 '20 at 07:09
  • Should I use `[ApiVersion("schooljob")]`? I tried using both` [HttpPost("{version:apiVersion}/getstudents")]` and `[Route("{version:apiVersion}")]` and nothing displayed. Also, I tried changing `version:apiVersion` as `version:schooljob` but swagger didn't run at all. Btw, in .net Core 3.1, i had to use `services.AddVersionedApiExplorer();` after `services.AddApiVersioning();`. The 500 error was gone, but nothing was displayed. – KaraKaplanKhan Sep 04 '20 at 07:23
  • Yes you should use `[ApiVersion("schooljob")]` and `[Route("{version:apiVersion}")]` on top of your controller, and `[HttpPost("getstudents")]` on top of your method. `[ApiVersion]` tells the API that this controller is only for the version `schooljob`. `[Route]` tells the API how to extract the version from the route. – Bruno Martins Sep 04 '20 at 07:52
  • At long last, I could run my application. Your suggestion helped a lot but several things were of. I needed to add my controller class `[ApiController]` attribute. I had to add `services.AddVersionedApiExplorer();` to my `startup` - `ConfigureServices`. I didn't need `{version:apiVersion}` but i used it anyway. And lastly, for the versioning I should have used `"1.0"` or `"2.0"` instead `"schooljob"` because there was a rule for versioning in .net core 3.1. Now, I will try to override this rule. Thanks for help. – KaraKaplanKhan Sep 08 '20 at 10:14
  • Well, I think your last call would be to create 2 json files and serves 2 distinct Swagger pages for this. Calling `app.UseSwaggerUI()` (with a different `options.RoutePrefix`) is enough for two distinct pages. The hard stuff is more about the 2 json files I think – Bruno Martins Sep 08 '20 at 12:42
0

use the below code in program.cs (.net 6), If you are using .net 5(or below version) use this code in strartup.cs

services.AddVersionedApiExplorer(options =>
    {
        options.GroupNameFormat = "'v'VV";
        options.SubstituteApiVersionInUrl = true;
    });


services.AddSwaggerGen(options =>
        {

            options.DocInclusionPredicate((documentName, apiDescription) =>
            {
                var actionApiVersionModel = apiDescription.ActionDescriptor.GetApiVersionModel();
                var apiExplorerSettingsAttribute = (ApiExplorerSettingsAttribute)apiDescription.ActionDescriptor.EndpointMetadata.First(x => x.GetType().Equals(typeof(ApiExplorerSettingsAttribute)));
                if (actionApiVersionModel == null)
                {
                    return true;
                }
                if (actionApiVersionModel.DeclaredApiVersions.Any())
                {
                    return actionApiVersionModel.DeclaredApiVersions.Any(v =>
                    $"{apiExplorerSettingsAttribute.GroupName}v{v}" == documentName);
                }
                return actionApiVersionModel.ImplementedApiVersions.Any(v =>
                       $"{apiExplorerSettingsAttribute.GroupName}v{v}" == documentName);
            });

            var apiVersionDescriptionProvider = services.BuildServiceProvider().GetService<IApiVersionDescriptionProvider>();
            foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions)
            {
                options.SwaggerDoc($"Orders{description.GroupName}", new OpenApiInfo
                {
                    Title = "Example Service",
                    Description = "Example Service -- Backend Service Project",
                    Version = description.ApiVersion.ToString(),
                    
                });
                options.SwaggerDoc($"Payments{description.GroupName}", new OpenApiInfo
                {
                    Title = "Example Service",
                    Description = "Example Service -- Backend Service Project",
                    Version = description.ApiVersion.ToString(),
                });
                // Orders & Payments are the groupName above ex: out put will be : swagger/OrdersV1.0/swagger.json ( if you select Orders from the swagger definition dropdown) 
                // If you select Payments => output : swagger/PaymentsV1.0/swagger.json
            }
            
            
var app = builder.Build();
                
            app.UseSwagger();

            app.UseSwaggerUI(
            swaggerOptions =>
            {                   
                foreach (var description in provider.ApiVersionDescriptions)
                {
                    swaggerOptions.SwaggerEndpoint($"{swagger/Orders{description.GroupName}/swagger.json",$"Orders  {description.GroupName.ToUpperInvariant()}" );
                    swaggerOptions.SwaggerEndpoint($"{swagger/Payments{description.GroupName}/swagger.json", $"Payments  {description.GroupName.ToUpperInvariant()}");
                }
            }); 
            
            
            

Controller //V1 // example controller 1:

[ApiVersion("1.0")]
[ApiController]
[ApiExplorerSettings(GroupName = "Orders")]
[Route("api/v{version:apiVersion}/[controller]")]
public class OrdersController : ControllerBase              
            

// example controller 2:

[ApiVersion("1.0")]
[ApiController]
[ApiExplorerSettings(GroupName = "Payments")]
[Route("api/v{version:apiVersion}/[controller]")]
public class PaymentsController : ControllerBase    
Shareef
  • 11
  • 1