aws-lambda-dotnet icon indicating copy to clipboard operation
aws-lambda-dotnet copied to clipboard

AWS Lambda - Supporting binary response in ASP.NET Core 6

Open hugohlln opened this issue 2 years ago • 6 comments

Describe the bug

Hello,

I'm creating an ASP.NET Core 6 Rest API running in a lambda behind an API Gateway. We have somme issues since we would like to return image/webp content and we are not able to do it.

We've seen that a solution exists for ASP.NET Core 2 by calling the following method : RegisterResponseContentEncodingForContentType("image/webp", ResponseContentEncoding.Base64);

But unforunately we didn't find any information or tutorial to do the same thing using ASP.NET Core 6 cause the lambda support is done as below in the Program.cs file : builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi);

Is there any way to register image/webp conte type for Base64 transformation in ASP.NET Core 6 using the builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi); method ?

Thanks

Expected Behavior

Be able to register content-types for Base64 transformation in ASP.NET Core 6 Program.cs file

Current Behavior

Unable to register content-types for Base64 transformation in ASP.NET Core 6 Program.cs file

Reproduction Steps

  • Create an ASP.NET Core 6 rest api
  • Add the lambda support in the Program.cs file (builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi);)
  • Return a webp image in your endpoint : [HttpGet] public ActionResult GetWebp() { var image = System.IO.File.ReadAllBytes("myImage.webp"); return File(image, "image/webp"); } }

Possible Solution

No response

Additional Information/Context

No response

AWS .NET SDK and/or Package version used

Amazon.Lambda.AspNetCoreServer.Hosting 1.6.0

Targeted .NET Platform

ASP.NET Core 6.0

Operating System and version

Any

hugohlln avatar Aug 30 '23 11:08 hugohlln

I'm facing the same issue, a solution would be great.

brignolij avatar Aug 30 '23 12:08 brignolij

@hugohlln Good morning. Thanks for opening the issue. Please refer to https://github.com/aws/aws-lambda-dotnet/discussions/1527, it might help. You need to use LambdaEventSource.HttpApi and return APIGatewayHttpApiV2ProxyResponse.

ashishdhingra avatar Aug 30 '23 17:08 ashishdhingra

Hi @ashishdhingra,

Thanks for your reply. We wan't to keep our current returned object ActionResult ant not APIGatewayHttpApiV2ProxyResponse in our controllers. The fact is that if we want in the future to run the project in another execution environment than a Lambda we will be obliged to edit our code and this is not the goal. There is no way to register image/webp to tranformation for Base64 when adding the lambda hosting service to the service collection ?

hugohlln avatar Aug 31 '23 07:08 hugohlln

What I did for the moment is creating my own AddAWSLambdaHosting service and my own APIGatewayRestApiLambdaRuntimeSupportServer where I can register my transformation for Base64 :

Custom APIGatewayRestApiLambdaRuntimeSupportServer :

public class APIGatewayRestApiLambdaRuntimeSupportServerCustom : LambdaRuntimeSupportServer
  {
      /// <summary>
      /// Create instances
      /// </summary>
      /// <param name="serviceProvider">The IServiceProvider created for the ASP.NET Core application</param>
      public APIGatewayRestApiLambdaRuntimeSupportServerCustom(IServiceProvider serviceProvider)
          : base(serviceProvider)
      {
      }

      /// <summary>
      /// Creates HandlerWrapper for processing events from API Gateway REST API
      /// </summary>
      /// <param name="serviceProvider"></param>
      /// <returns></returns>
      protected override HandlerWrapper CreateHandlerWrapper(IServiceProvider serviceProvider)
      {
          var handler = new APIGatewayRestApiMinimalApi(serviceProvider).FunctionHandlerAsync;
          return HandlerWrapper.GetHandlerWrapper(handler, serviceProvider.GetRequiredService<ILambdaSerializer>());
      }

      /// <summary>
      /// Create the APIGatewayProxyFunction passing in the ASP.NET Core application's IServiceProvider
      /// </summary>
      public class APIGatewayRestApiMinimalApi : APIGatewayProxyFunction
      {
          /// <summary>
          /// Create instances
          /// </summary>
          /// <param name="serviceProvider">The IServiceProvider created for the ASP.NET Core application</param>
          public APIGatewayRestApiMinimalApi(IServiceProvider serviceProvider)
              : base(serviceProvider)
          {
              this.RegisterResponseContentEncodingForContentType("image/webp", ResponseContentEncoding.Base64);
          }
      }
  }

Custom AddAWSLambdaHosting:

/// <summary>
    /// Enum for the possible event sources that will send HTTP request into the ASP.NET Core Lambda function.
    /// </summary>
    public enum LambdaEventSourceCustom
    {
        /// <summary>
        /// API Gateway REST API
        /// </summary>
        RestApi,

        /// <summary>
        /// API Gateway HTTP API
        /// </summary>
        HttpApi,

        /// <summary>
        /// ELB Application Load Balancer
        /// </summary>
        ApplicationLoadBalancer,

        /// <summary>
        /// API Gateway REST API Custom
        /// </summary>
        RestApiCustom
    }

    /// <summary>
    /// Extension methods to IServiceCollection. 
    /// </summary>
    public static class ServiceCollectionExtensions
    {
        /// <summary>
        /// Add the ability to run the ASP.NET Core Lambda function in AWS Lambda. If the project is not running in Lambda 
        /// this method will do nothing allowing the normal Kestrel webserver to host the application.
        /// </summary>
        /// <param name="services"></param>
        /// <param name="eventSource"></param>
        /// <returns></returns>
        public static IServiceCollection AddAWSLambdaHostingCustom(this IServiceCollection services, LambdaEventSourceCustom eventSource)
        {
            // Not running in Lambda so exit and let Kestrel be the web server
            return services.AddAWSLambdaHostingCustom(eventSource, (Action<HostingOptions>?)null);
        }

        /// <summary>
        /// Add the ability to run the ASP.NET Core Lambda function in AWS Lambda. If the project is not running in Lambda 
        /// this method will do nothing allowing the normal Kestrel webserver to host the application.
        /// </summary>
        /// <param name="services"></param>
        /// <param name="eventSource"></param>
        /// <param name="configure"></param>
        /// <returns></returns>
        public static IServiceCollection AddAWSLambdaHostingCustom(this IServiceCollection services, LambdaEventSourceCustom eventSource, Action<HostingOptions>? configure = null)
        {
            // Not running in Lambda so exit and let Kestrel be the web server
            if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("AWS_LAMBDA_FUNCTION_NAME")))
                return services;

            var hostingOptions = new HostingOptions();

            if (configure != null)
                configure.Invoke(hostingOptions);

            services.TryAddSingleton<ILambdaSerializer>(hostingOptions.Serializer ?? new DefaultLambdaJsonSerializer());

            var serverType = eventSource switch
            {
                LambdaEventSourceCustom.HttpApi => typeof(APIGatewayHttpApiV2LambdaRuntimeSupportServer),
                LambdaEventSourceCustom.RestApi => typeof(APIGatewayRestApiLambdaRuntimeSupportServer),
                LambdaEventSourceCustom.ApplicationLoadBalancer => typeof(ApplicationLoadBalancerLambdaRuntimeSupportServer),
                LambdaEventSourceCustom.RestApiCustom => typeof(APIGatewayRestApiLambdaRuntimeSupportServerCustom),
                _ => throw new ArgumentException($"Event source type {eventSource} unknown")
            };

            Utilities.EnsureLambdaServerRegistered(services, serverType);

            return services;
        }
    }

And in my Program.cs:

builder.Services.AddAWSLambdaHostingCustom(LambdaEventSourceCustom.RestApiCustom);

hugohlln avatar Aug 31 '23 08:08 hugohlln

I really hope that a best solution will be provided and that we will be able to add those kind of transformation directly in the Program.cs file when adding the service to the service collection

hugohlln avatar Aug 31 '23 08:08 hugohlln

@hugohlln very good idea, I will try it too.

brignolij avatar Aug 31 '23 12:08 brignolij