admin管理员组

文章数量:1025289

I'm writing C# Lambda functions to be exposed by the API Gateway service, using the AWS Annotation Framework.

I successfully registered application services in the ConfigureServices(IServiceCollection services) method of the Startup.cs file.

In order to add some API configurations in the header of all incoming requests (Authorization header, etc.), I registered a middleware via the Configure(IApplicationBuilder app, IWebHostEnvironment env) method of the the Startup.cs file.

The problem is, the application is completely ignoring the middleware; in other terms, the application never passes through the middleware.

Here is my code:

Lambda function (in Function.cs file):

using Amazon.Lambda.Core;
using Amazon.Lambda.Annotations;
using TbTypes.Model.Api.Requests;
using Microsoft.AspNetCore.Mvc;
using FromServicesAttribute = Amazon.Lambda.Annotations.FromServicesAttribute;
using FromBodyAttribute = Microsoft.AspNetCore.Mvc.FromBodyAttribute;
using TbTypes.Services.Thingsboard;
using TbTypes.Model.Api.Reponses;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace TbInterface;

public class Function
{
    /// <summary>
    /// Function for requesting TB Devices visible by a User
    /// </summary>
    [LambdaFunction()]
    [HttpGet("/user/devices/")]
    public async Task<DevicesPage> FetchDevices(
        [FromBody] UserDevicesRequestBody body,
        ILambdaContext context,
        [FromServices] IDeviceService service)
    {
       // you can replace IDeviceService by a dummy service when reproducing the issue
        return await service.FetchDevices(body.claims.TbUserID, body.claims.CognitoUserID);
    }
}

My Startups.cs file with services registration in ConfigureServices() and middleware registration in Configure() method:

using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
using Amazon.Lambda.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Diagnostics;
using TbInterface.Configuration;
using TbInterface.Middlewares;
using TbInterface.Repositories;
using TbInterface.Services.Cache;
using TbInterface.Services.Database;
using TbInterface.Services.Thingsboard;
using TbTypes.Configuration;
using TbTypes.Repositories;
using TbTypes.Services.Cache;
using TbTypes.Services.Thingsboard;

namespace TbInterface
{
    [LambdaStartup]
    public class Startup
    {
        private IConfiguration Configuration;

        public Startup()
        {
            Configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddEnvironmentVariables()
            .Build();
        }

        public void ConfigureServices(IServiceCollection services)
        {

            // Application Configurations
            services.AddSingleton<IConfiguration>(implementationInstance: Configuration);
            services.AddSingleton<ITbConfiguration>(sp => {
                var settings = sp.GetRequiredService<IConfiguration>();
                return new TbConfiguration(settings);
            });

            // Cache Service: AWS Elasti Cache
            services.AddSingleton<IElastiCacheService, ElastiCacheService>();

            // Database Service: AWS DynamoDB
            services.AddSingleton<IAmazonDynamoDB, DynamoDB>();
            services.AddAWSService<IAmazonDynamoDB>();
            //services.AddDefaultAWSOptions(Configuration.GetAWSOptions());
            services.AddAWSService<Amazon.S3.IAmazonS3>();
            services.AddAWSService<IAmazonDynamoDB>();


            // Repositories
            services.AddSingleton<IUserTokenRepository, UserTokenRepository>();

            // Thingsboard API services
            services.AddSingleton<IAuthTokenService, AuthTokenService>();
            services.AddSingleton<IUserService, UserService>();
            services.AddSingleton<IDeviceService, DeviceService>();
            services.AddTransient<TbApiConfigurationMiddleware>();

        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
           // Here's how I resgistered the middleware
            app.UseMiddleware<TbApiConfigurationMiddleware>();
        }

    }
}

The middleware itself - TbApiConfigurationMiddleware.cs :

using Microsoft.AspNetCore.Http;
using System.Net;
using Newtonsoft.Json;
using TbAPIClient.Generated.Client;
using TbAPIClient.Generated.Model;
using TbClientConfiguration = TbAPIClient.Generated.Client.Configuration;
using TbTypes.Model.Api.Requests;
using TbTypes.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace TbInterface.Middlewares
{
    public class TbApiConfigurationMiddleware : IMiddleware
    {
        /// <summary>
        /// Custom logic to be executed before the next middleware
        /// </summary>
        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            var authService = context.RequestServices.GetService<IAuthTokenService>();

            BaseApiRequestBody? body = extractRequestBody(context);

            if (body == null || body.claims == null) {
                await handleBadRequestBody(context, "Bad request body format");
                return;
            }

            //JwtPair? token = await _authService.RetrieveOrRequestUserToken(body.claims.TbUserID!);
            JwtPair? token = await authService!.FetchUserTokenAsync(body.claims.TbUserID!);

            if (token == null)
            {
                await handleUnauthorizedUser(context);
                return;
            }

            var tbConfiguration = context.RequestServices.GetService<ITbConfiguration>();
            ConfigureTbApiToken(token!, tbConfiguration!);

            await next(context);
        }

        /// <summary>
        /// Extract request body to perform basic format validation
        /// </summary>
        /// <param name="context">HTTP Context</param>
        /// <returns></returns>
        private BaseApiRequestBody? extractRequestBody(HttpContext context) {
            var rawBody = context.Request.Body.ToString();

            if (rawBody == null)
            {
                return null;
            }

            return JsonConvert.DeserializeObject<BaseApiRequestBody>(rawBody);
        }

        /// <summary>
        /// Handling bad request body
        /// </summary>
        /// <param name="context">HTTP Context</param>
        /// <returns></returns>
        private async Task handleBadRequestBody(HttpContext context, string message) {
            context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
            context.Response.ContentType = "application/json";
            var body = new ApiException(context.Response.StatusCode, message);
            await context.Response.WriteAsync(JsonConvert.SerializeObject(body));
        }


        /// <summary>
        /// Configuring middleware response in case of Unauthorized User
        /// </summary>
        /// <param name="context">HTTP Context</param>
        /// <returns></returns>
        private async Task handleUnauthorizedUser(HttpContext context)
        {
            context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            context.Response.ContentType = "application/json";
            var body = new ApiException(context.Response.StatusCode, "Unauthorized user");
            await context.Response.WriteAsync(JsonConvert.SerializeObject(body));
        }

        /// <summary>
        /// Method for configuring Thingsboard API Auth Token
        /// </summary>
        /// <param name="token">Token definition: {Token, RefreshToken} </param>
        /// <param name="tbConfiguration">Application configs </param>
        /// <returns></returns>
        private void ConfigureTbApiToken(JwtPair token, ITbConfiguration tbConfiguration)
        {
            TbClientConfiguration.Default.ApiKeyPrefix[tbConfiguration.TokenHeaderKey] = tbConfiguration.TokenType;
            TbClientConfiguration.Default.ApiKey[tbConfiguration.TokenHeaderKey] = (string)token.Token;
        }
    }
}

I'm writing C# Lambda functions to be exposed by the API Gateway service, using the AWS Annotation Framework.

I successfully registered application services in the ConfigureServices(IServiceCollection services) method of the Startup.cs file.

In order to add some API configurations in the header of all incoming requests (Authorization header, etc.), I registered a middleware via the Configure(IApplicationBuilder app, IWebHostEnvironment env) method of the the Startup.cs file.

The problem is, the application is completely ignoring the middleware; in other terms, the application never passes through the middleware.

Here is my code:

Lambda function (in Function.cs file):

using Amazon.Lambda.Core;
using Amazon.Lambda.Annotations;
using TbTypes.Model.Api.Requests;
using Microsoft.AspNetCore.Mvc;
using FromServicesAttribute = Amazon.Lambda.Annotations.FromServicesAttribute;
using FromBodyAttribute = Microsoft.AspNetCore.Mvc.FromBodyAttribute;
using TbTypes.Services.Thingsboard;
using TbTypes.Model.Api.Reponses;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace TbInterface;

public class Function
{
    /// <summary>
    /// Function for requesting TB Devices visible by a User
    /// </summary>
    [LambdaFunction()]
    [HttpGet("/user/devices/")]
    public async Task<DevicesPage> FetchDevices(
        [FromBody] UserDevicesRequestBody body,
        ILambdaContext context,
        [FromServices] IDeviceService service)
    {
       // you can replace IDeviceService by a dummy service when reproducing the issue
        return await service.FetchDevices(body.claims.TbUserID, body.claims.CognitoUserID);
    }
}

My Startups.cs file with services registration in ConfigureServices() and middleware registration in Configure() method:

using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
using Amazon.Lambda.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Diagnostics;
using TbInterface.Configuration;
using TbInterface.Middlewares;
using TbInterface.Repositories;
using TbInterface.Services.Cache;
using TbInterface.Services.Database;
using TbInterface.Services.Thingsboard;
using TbTypes.Configuration;
using TbTypes.Repositories;
using TbTypes.Services.Cache;
using TbTypes.Services.Thingsboard;

namespace TbInterface
{
    [LambdaStartup]
    public class Startup
    {
        private IConfiguration Configuration;

        public Startup()
        {
            Configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddEnvironmentVariables()
            .Build();
        }

        public void ConfigureServices(IServiceCollection services)
        {

            // Application Configurations
            services.AddSingleton<IConfiguration>(implementationInstance: Configuration);
            services.AddSingleton<ITbConfiguration>(sp => {
                var settings = sp.GetRequiredService<IConfiguration>();
                return new TbConfiguration(settings);
            });

            // Cache Service: AWS Elasti Cache
            services.AddSingleton<IElastiCacheService, ElastiCacheService>();

            // Database Service: AWS DynamoDB
            services.AddSingleton<IAmazonDynamoDB, DynamoDB>();
            services.AddAWSService<IAmazonDynamoDB>();
            //services.AddDefaultAWSOptions(Configuration.GetAWSOptions());
            services.AddAWSService<Amazon.S3.IAmazonS3>();
            services.AddAWSService<IAmazonDynamoDB>();


            // Repositories
            services.AddSingleton<IUserTokenRepository, UserTokenRepository>();

            // Thingsboard API services
            services.AddSingleton<IAuthTokenService, AuthTokenService>();
            services.AddSingleton<IUserService, UserService>();
            services.AddSingleton<IDeviceService, DeviceService>();
            services.AddTransient<TbApiConfigurationMiddleware>();

        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
           // Here's how I resgistered the middleware
            app.UseMiddleware<TbApiConfigurationMiddleware>();
        }

    }
}

The middleware itself - TbApiConfigurationMiddleware.cs :

using Microsoft.AspNetCore.Http;
using System.Net;
using Newtonsoft.Json;
using TbAPIClient.Generated.Client;
using TbAPIClient.Generated.Model;
using TbClientConfiguration = TbAPIClient.Generated.Client.Configuration;
using TbTypes.Model.Api.Requests;
using TbTypes.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace TbInterface.Middlewares
{
    public class TbApiConfigurationMiddleware : IMiddleware
    {
        /// <summary>
        /// Custom logic to be executed before the next middleware
        /// </summary>
        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            var authService = context.RequestServices.GetService<IAuthTokenService>();

            BaseApiRequestBody? body = extractRequestBody(context);

            if (body == null || body.claims == null) {
                await handleBadRequestBody(context, "Bad request body format");
                return;
            }

            //JwtPair? token = await _authService.RetrieveOrRequestUserToken(body.claims.TbUserID!);
            JwtPair? token = await authService!.FetchUserTokenAsync(body.claims.TbUserID!);

            if (token == null)
            {
                await handleUnauthorizedUser(context);
                return;
            }

            var tbConfiguration = context.RequestServices.GetService<ITbConfiguration>();
            ConfigureTbApiToken(token!, tbConfiguration!);

            await next(context);
        }

        /// <summary>
        /// Extract request body to perform basic format validation
        /// </summary>
        /// <param name="context">HTTP Context</param>
        /// <returns></returns>
        private BaseApiRequestBody? extractRequestBody(HttpContext context) {
            var rawBody = context.Request.Body.ToString();

            if (rawBody == null)
            {
                return null;
            }

            return JsonConvert.DeserializeObject<BaseApiRequestBody>(rawBody);
        }

        /// <summary>
        /// Handling bad request body
        /// </summary>
        /// <param name="context">HTTP Context</param>
        /// <returns></returns>
        private async Task handleBadRequestBody(HttpContext context, string message) {
            context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
            context.Response.ContentType = "application/json";
            var body = new ApiException(context.Response.StatusCode, message);
            await context.Response.WriteAsync(JsonConvert.SerializeObject(body));
        }


        /// <summary>
        /// Configuring middleware response in case of Unauthorized User
        /// </summary>
        /// <param name="context">HTTP Context</param>
        /// <returns></returns>
        private async Task handleUnauthorizedUser(HttpContext context)
        {
            context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            context.Response.ContentType = "application/json";
            var body = new ApiException(context.Response.StatusCode, "Unauthorized user");
            await context.Response.WriteAsync(JsonConvert.SerializeObject(body));
        }

        /// <summary>
        /// Method for configuring Thingsboard API Auth Token
        /// </summary>
        /// <param name="token">Token definition: {Token, RefreshToken} </param>
        /// <param name="tbConfiguration">Application configs </param>
        /// <returns></returns>
        private void ConfigureTbApiToken(JwtPair token, ITbConfiguration tbConfiguration)
        {
            TbClientConfiguration.Default.ApiKeyPrefix[tbConfiguration.TokenHeaderKey] = tbConfiguration.TokenType;
            TbClientConfiguration.Default.ApiKey[tbConfiguration.TokenHeaderKey] = (string)token.Token;
        }
    }
}

本文标签: C AWS Lambda Annotation Functions never passes through middlewareStack Overflow