Asp.NET Core 3.1 and Swashbuckle.AspNetCore.Swagger

11,829

After hours and hours digging I found that with Microsoft.OpenApi 1.2.0-preview isn't compactibile with .net core 3.1 and swagger v5, use v1.1.4 of Microsoft OpenApi

Share:
11,829
Wasyster
Author by

Wasyster

Updated on June 24, 2022

Comments

  • Wasyster
    Wasyster almost 2 years

    I updated my asp.net core app to v3.1 and updated my Swagger form v5.04 rc to v5.0, and everything stopped to work. The API is working, but I can't generate swagger file, and I was able.

    Here is my swagger configuration.

    public static class SwaggerConfig
        {
            [Obsolete]
            public static void ConfigureSWAGGER(this IServiceCollection serviceColletion)
            {
                if (serviceColletion == null)
                {
                    throw new ArgumentNullException(nameof(serviceColletion));
                }
    
                // Use http://localhost:5000/swagger/ui/index to inspect API docs
                serviceColletion.AddSwaggerGen(x =>
                {
                    x.SwaggerDoc(Constatns.BackofficeSwaggerGroup, new OpenApiInfo { Title = "Portal Nekretnine - Backoffice", Version = Constatns.BackofficeSwaggerGroup });
                    x.SwaggerDoc(Constatns.PublicSwaggerGroup, new OpenApiInfo { Title = "Portal Nekretnine - Public", Version = Constatns.PublicSwaggerGroup });
    
                    // This code allow you to use XML-comments
                    string xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                    string xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                    if (File.Exists(xmlPath))
                    {
                        x.IncludeXmlComments(xmlPath);
                    }
    
                    x.AddSecurityDefinition
                    (
                        "Bearer",
                        new OpenApiSecurityScheme
                        {
                            In = ParameterLocation.Header,
                            Description = "Please enter token into the field",
                            Name = "Authorization",
                            Type = SecuritySchemeType.ApiKey
                        }
                    );
                });
            }
    
            public static void UseSWAGGER(this IApplicationBuilder applicationBuilder)
            {
                // Enable middleware to serve generated Swagger as a JSON endpoint.
                applicationBuilder.UseSwagger();
    
                // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
                // specifying the Swagger JSON endpoint.
                applicationBuilder.UseSwaggerUI(x =>
                {
                    x.SwaggerEndpoint($"/swagger/{Constatns.BackofficeSwaggerGroup}/swagger.json", "Portal Nekretnine - Backoffice");
                    x.SwaggerEndpoint($"/swagger/{Constatns.PublicSwaggerGroup}/swagger.json", "Portal Nekretnine - Public");
                });
            }
        }
    

    And this is my startup.cs

    public partial class Startup
        {
            public IConfiguration Configuration { get; }
    
            public Startup(IConfiguration configuration, IWebHostEnvironment env)
            {
                Configuration = configuration;
    
                if (env.IsDevelopment())
                {
                    IConfigurationBuilder builder = new ConfigurationBuilder()
                                                                            .SetBasePath(env.ContentRootPath)
                                                                            .AddJsonFile($"appsettings.development.json", optional: true)
                                                                            .AddEnvironmentVariables();
    
                    Configuration = builder.Build();
                }
                else
                {
                    IConfigurationBuilder builder = new ConfigurationBuilder()
                                                                            .AddJsonFile($"appsettings.production.json", optional: true)
                                                                            .AddEnvironmentVariables();
    
                    Configuration = builder.Build();
                }
            }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            [System.Obsolete]
            public void ConfigureServices(IServiceCollection services)
            {
                SetServices(services);
    
                services.ConfigureCOMPRESSION();
    
                //disable built in model validator
                services.Configure<ApiBehaviorOptions>(options =>
                {
                    options.SuppressModelStateInvalidFilter = true;
                });
    
                services.AddMvcCore(options =>
                                {
                                    //Validate model
                                    options.Filters.Add(typeof(ValidateModelAttribute));
    
                                    // Add "Cache-Control" header
                                    options.Filters.Add(typeof(CacheControlFilter));
    
                                    // Add custom binder provider for mapping json object form multipart/form-data
                                    options.ModelBinderProviders.Insert(0, new JsonModelBinderProvider());
                                })
                                .SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
                                .AddApiExplorer();
    
                services.AddControllers()
                                .AddNewtonsoftJson(options =>
                                {
                                    options.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
                                    options.SerializerSettings.Converters.Add(new JsonDateConverter());
                                    options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                                    options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
    #if DEBUG
                                    options.SerializerSettings.Formatting = Formatting.Indented;
    #else
                                    options.SerializerSettings.Formatting = Formatting.None;
    #endif
                                });
    
                services.Configure<ForwardedHeadersOptions>(options =>
                {
                    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
                });
    
                services.ConfigureCookieAuthentication();
                services.ConfigureAUTH(Configuration);
    
                services.ConfigureCORS();
    
                services.ConfigureSWAGGER();
                services.AddSwaggerGenNewtonsoftSupport(); // explicit opt-in - needs to be placed after AddSwaggerGen()
            }
    
            // 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();
                }
                else
                {
                    app.UseHsts();
                }
    
                app.UseRouting();
    
                app.UseCOMPRESSION();
    
                app.UseAUTH();
                app.UseAuthorization();
    
                app.UseCORS();
                app.UseSWAGGER();
    
                app.UseHttpStatusCodeExceptionMiddleware();
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers();
                });
            }
        }
    

    and an example of my controllers:

    [ApiController]
    public class SecurityController : ControllerBase
    {
    
            [ApiExplorerSettings(GroupName = Constatns.PublicSwaggerGroup)]
            [SwaggerOperation(OperationId = "registerVisitor")]
            [HttpPost("api/register/visitor")]
            [ValidateModel]
            [AllowAnonymous]
            [ProducesResponseType((int)HttpResponseType.OK, Type = typeof(TokenResponse))]
            [ProducesResponseType((int)HttpResponseType.BadRequest)]
            [Produces("application/json")]
            public async Task<TokenResponse> RegisterVisitor([FromBody] RegisterVisitorRequest data)
            {}
    }
    

    The error that I am getting are:

    fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[1] An unhandled exception has occurred while executing the request. System.MissingMethodException: Method not found: 'Void Microsoft.OpenApi.Writers.OpenApiJsonWriter..ctor(System.IO.TextWriter)'. at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.RespondWithSwaggerJson(HttpResponse response, OpenApiDocument swagger) at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.RespondWithSwaggerJson(HttpResponse response, OpenApiDocument swagger) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)

    thnx