aws / aws-lambda-dotnet

Libraries, samples and tools to help .NET Core developers develop AWS Lambda functions.
Apache License 2.0
1.58k stars 479 forks source link

Swashbuckle.AspNetCore.Filters is not working in .NET6 ASP.NET Core Minimal API Serverless Application #1236

Closed noopd13 closed 2 years ago

noopd13 commented 2 years ago

Describe the bug

Hi I am using ASP.NET Core Minimal API Serverless Application

this is my current .csproj ,

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
    <AWSProjectType>Lambda</AWSProjectType>
    <!-- This property makes the build directory similar to a publish directory and helps the AWS .NET Lambda Mock Test Tool find project dependencies. -->
    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
    <!-- Generate ready to run images during publishing to improvement cold starts. -->
    <PublishReadyToRun>true</PublishReadyToRun>
    <GenerateDocumentationFile>True</GenerateDocumentationFile>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <WarningLevel>0</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
    <WarningLevel>0</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Amazon.Lambda.AspNetCoreServer.Hosting" Version="1.3.1" />
    <PackageReference Include="AWSSDK.Extensions.NETCore.Setup" Version="3.7.2" />
    <PackageReference Include="AWSSDK.SimpleNotificationService" Version="3.7.3.77" />
    <PackageReference Include="AWSSDK.SQS" Version="3.7.2.74" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.6" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.6">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
    <PackageReference Include="StackExchange.Redis" Version="2.6.48" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.1" />
    <PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.3" />
  </ItemGroup>
</Project>

I have just added Swashbuckle.AspNetCore.Filters for adding request examples for the swagger page , now i am not able to load swagger page it shows internal server error

The error that is logged in cloudwatch : System.TypeLoadException: Could not load type 'Swashbuckle.AspNetCore.Filters.IMultipleExamplesProvider`1' from assembly 'Swashbuckle.AspNetCore.Filters, Version=7.0.3.0,

the page is working fine in local , and if i don't use the 'Swashbuckle.AspNetCore.Filters' the lambda is working fine

Expected Behavior

The swagger page should work with Swashbuckle.AspNetCore.Filters package dependency

Current Behavior

The error that is logged in cloudwatch : System.TypeLoadException: Could not load type 'Swashbuckle.AspNetCore.Filters.IMultipleExamplesProvider`1' from assembly 'Swashbuckle.AspNetCore.Filters, Version=7.0.3.0,

Reproduction Steps

public class Personrequest: IMultipleExamplesProvider<StockImageRequest>
    {
        public IEnumerable<SwaggerExample<person>> GetExamples()
        {
            yield return SwaggerExample.Create(
                "Mandatory images",
                new person { name="xyz"}
                );
        }
    }

System.TypeLoadException: Could not load type 'Swashbuckle.AspNetCore.Filters.IMultipleExamplesProvider`

Possible Solution

No response

Additional Information/Context

No response

AWS .NET SDK and/or Package version used

<PackageReference Include="Amazon.Lambda.AspNetCoreServer.Hosting" Version="1.3.1" />
<PackageReference Include="AWSSDK.Extensions.NETCore.Setup" Version="3.7.2" />
<PackageReference Include="AWSSDK.SimpleNotificationService" Version="3.7.3.77" />
<PackageReference Include="AWSSDK.SQS" Version="3.7.2.74" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.6">
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="StackExchange.Redis" Version="2.6.48" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.1" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.3" />

Targeted .NET Platform

.NET6

Operating System and version

Amazon Linux

ashishdhingra commented 2 years ago

Hi @noopd13,

Good afternoon.

Could you please share the minimal code solution to reproduce the issue? Also, was it working earlier in Lambda environment before you upgraded minimal API?

Thanks, Ashish

noopd13 commented 2 years ago

Hi @ashishdhingra , have added my solution code . have developed these API's based on .net 6 minimal api , was never upgraded to minimal api , it was working fine in lambda environment until i added aspnetcore.filters dependency in the code

Program.cs

`using Amazon.SimpleNotificationService;
using MFCimages.API.DB.Contexts;
using MFCimages.API.Examples
using MFCimages.API.Middleware;
using MFCimages.API.Services;
using Microsoft.EntityFrameworkCore;
using StackExchange.Redis;
using Swashbuckle.AspNetCore.Filters;
using System.Reflection;
using System.Text.Json.Serialization;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers().AddJsonOptions(options => { // show enum value in swagger.
                                                              options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); });;

// Add AWS Lambda support. When application is run in Lambda Kestrel is swapped out as the web server with Amazon.Lambda.AspNetCoreServer. This
// package will act as the webserver translating request and responses between the Lambda event source and ASP.NET Core.
builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi);
builder.Services.AddDefaultAWSOptions(builder.Configuration.GetAWSOptions());
builder.Services.AddAWSService<IAmazonSimpleNotificationService>();

builder.Services.AddDbContext<ImageContext>(
    options => options.UseSqlServer(builder.Configuration.GetConnectionString("")));
var multiplexer = ConnectionMultiplexer.Connect(builder.Configuration.GetValue<string>(""));
builder.Services.AddSingleton<IConnectionMultiplexer>(multiplexer);

builder.Services.AddSwaggerGen(c => {
    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    c.IncludeXmlComments(xmlPath);
    c.ExampleFilters(); // add this to support examples_
});
_builder.Services.AddSwaggerExamplesFromAssemblyOf<PersonRequestExample>();_
builder.Services.AddTransient<IImageServices, ImageService>();
builder.Services.AddTransient<IVideoService, VideoService>();

var app = builder.Build();

app.UseSwagger();
app.UseSwaggerUI(o=>{
    o.DisplayRequestDuration();

});
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseMiddleware<RequestLoggingMiddleware>();
app.MapControllers();
app.Run();

PersonRequestExample.cs

using MFCimages.API.DTO;
using Newtonsoft.Json;
using Swashbuckle.AspNetCore.Filters;

namespace MFCimages.API.Examples
{

   public class PersonRequestExample: IMultipleExamplesProvider<PersonRequest>
    {
        public IEnumerable<SwaggerExample<person>> GetExamples()
        {
            yield return SwaggerExample.Create(
                "Mandatory images",
                new person { name="xyz"}
                );
        }
    }
}
ashishdhingra commented 2 years ago

@noopd13 Thanks for sharing the example. Unfortunately, I'm unable to reproduce the issue using ASP.NET Core Minimal API serverless application template. Below is the example code and output. .csproj

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
    <AWSProjectType>Lambda</AWSProjectType>
    <!-- This property makes the build directory similar to a publish directory and helps the AWS .NET Lambda Mock Test Tool find project dependencies. -->
    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
    <!-- Generate ready to run images during publishing to improvement cold starts. -->
    <PublishReadyToRun>true</PublishReadyToRun>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Amazon.Lambda.AspNetCoreServer.Hosting" Version="1.3.1" />
    <PackageReference Include="AWSSDK.Extensions.NETCore.Setup" Version="3.7.2" />
    <PackageReference Include="AWSSDK.SimpleNotificationService" Version="3.7.3.77" />
    <PackageReference Include="AWSSDK.SQS" Version="3.7.2.74" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.6" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.6">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
    <PackageReference Include="StackExchange.Redis" Version="2.6.48" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.1" />
    <PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.3" />
  </ItemGroup>
</Project>

Program.cs

using Amazon.SimpleNotificationService;
using ASPNETCoreMinimalAPI;
using Swashbuckle.AspNetCore.Filters;

var builder = WebApplication.CreateBuilder(args);

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

// Add AWS Lambda support. When application is run in Lambda Kestrel is swapped out as the web server with Amazon.Lambda.AspNetCoreServer. This
// package will act as the webserver translating request and responses between the Lambda event source and ASP.NET Core.
builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi);
builder.Services.AddDefaultAWSOptions(builder.Configuration.GetAWSOptions());
builder.Services.AddAWSService<IAmazonSimpleNotificationService>();

builder.Services.AddSwaggerGen(c => c.ExampleFilters());
builder.Services.AddSwaggerExamplesFromAssemblyOf<PersonExample>();

var app = builder.Build();

app.UseSwagger();
app.UseSwaggerUI(o => {
    o.DisplayRequestDuration();
    o.SwaggerEndpoint("/Prod/swagger/v1/swagger.json", "My API V1");
});

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.MapGet("/", () => "Welcome to running ASP.NET Core Minimal API on AWS Lambda");

app.Run();

PersonRequestExample.cs

using Swashbuckle.AspNetCore.Filters;

namespace ASPNETCoreMinimalAPI
{
    public class PersonExample : IMultipleExamplesProvider<Person>
    {
        public IEnumerable<SwaggerExample<Person>> GetExamples()
        {
            yield return SwaggerExample.Create(
                "Mandatory images",
                new Person { Name = "xyz" }
                );
        }
    }

    public class Person
    {
        public string? Name { get; set; }
    }
}

Controllers\PersonController.cs

using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Filters;

namespace ASPNETCoreMinimalAPI.Controllers;

[ApiController]
[Route("[controller]")]
public class PersonController : ControllerBase
{
    private readonly ILogger<PersonController> _logger;

    public PersonController(ILogger<PersonController> logger)
    {
        _logger = logger;
    }

    [HttpGet(Name = "GetPerson")]
    [SwaggerResponseExample(200, typeof(PersonExample))]
    public Person Get()
    {
        return new Person() { 
            Name = "Test Person"
        };
    }
}

Invoking the URL https://XXXXXXXX.execute-api.us-east-2.amazonaws.com/Prod/swagger/index.html successfully displays the below Swagger UI:

Screen Shot 2022-07-11 at 11 29 24 AM

No error is logged in CloudWatch logs.

(I'm unsure if your PersonRequestExample is correct. I followed an example available online)

References:

Thanks, Ashish

noopd13 commented 2 years ago

hi @ashishdhingra , i have used the filter examples as a part of request

 [HttpGet(Name = "GetPerson")]
    [SwaggerRequestExample(typeof(Person), typeof(PersonExample))]
    public Person Get()
    {
        return new Person() { 
            Name = "Test Person"
        };
    }
noopd13 commented 2 years ago

hI @ashishdhingra , adding the reference of Microsoft.OpenApi fixed the issue thanks

github-actions[bot] commented 2 years ago

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see. If you need more assistance, please either tag a team member or open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.