title: AWS Lambda with mapping of web socket routes to http endpoints in controllers and testing
body: <p>I'm new to AWS Lambda, fascinating stuff. I am playing with some scenarios integrating AWS Lambda developed with <code>netcoreapp3.1</code> with AWS API Gateway.
  I saw AWS Api Gateway supports web socket management, which is great because it deals with keeping connections open and sends any frame to the back-end lambda, and exposes some secured POST endpoint to send messages from back-end lambda to the client connected to the gateway's web socket. All the heavy lifting is there.</p>
  <p>I also saw that there is some package for C# developers that allows using lambda applications as if they were a standard web api. See <a href="https://aws.amazon.com/blogs/compute/announcing-aws-lambda-supports-for-net-core-3-1/" rel="nofollow noreferrer">https://aws.amazon.com/blogs/compute/announcing-aws-lambda-supports-for-net-core-3-1</a>
  This is fantastic because I honestly don't get why some developers always try to have one lambda per endpoint, when a lambda can be used to route internally to whichever endpoint, and use all the goodness of the generic hosting for aspnet core 3.1+ plus logging, dependency injection container out of the box, etc. and a very nice way to test everything as if it was a web api application without worrying about the lambda entry point. See the package <a href="https://github.com/aws/aws-lambda-dotnet/tree/master/Libraries/src/Amazon.Lambda.AspNetCoreServer" rel="nofollow noreferrer">https://github.com/aws/aws-lambda-dotnet/tree/master/Libraries/src/Amazon.Lambda.AspNetCoreServer</a></p>
  <p>The lambda entry point looks like this. As you can see, I just inherit from the package's <code>APIGatewayProxyFunction</code> class and everything uses the same as if I ran it locally as a kestrel web app.</p>
  <pre><code>public class LambdaEntryPoint
      : APIGatewayProxyFunction
  {
      protected override void Init(IWebHostBuilder builder)
      {
          builder
              .UseStartup&lt;Startup&gt;();
      }
  }
  </code></pre>
  <p>The <code>Program.cs</code> or local entry point when running it as a kestrel web app</p>
  <pre><code>public static class LocalEntryPoint
  {
      private static void Main(string[] args)
      {
          CreateHostBuilder(args).Build().Run();
      }
  
      private static IHostBuilder CreateHostBuilder(string[] args)
      {
          return Host
              .CreateDefaultBuilder(args)
              .ConfigureWebHostDefaults(
                  webBuilder =&gt; { webBuilder.UseStartup&lt;Startup&gt;(); });
      }
  }
  </code></pre>
  <p>The <code>Startup.cs</code> looks like any standard Startup, with registrations and http pipeline, and knows nothing about whether it's being executed as a lambda or as a standard web app (great, isn't it? If only I could use this in a nice way for web sockets and test it...)</p>
  <pre><code>public class Startup
  {
      public Startup(IConfiguration configuration)
      {
          Configuration = configuration;
      }
  
      public IConfiguration Configuration { get; }
  
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddControllers();
      }
  
      public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
      {
          app.UseRouting();
          app.UseAuthorization();
          app.UseEndpoints(endpoints =&gt; { endpoints.MapControllers(); });
      }
  }
  </code></pre>
  <p>Having said that, <strong>I haven't been lucky to find any example on how to use an AWS Lambda with AspNetCore for web socket connectivity</strong>.</p>
  <p>Ideally I'd like my lambda application to be a web api with local and lambda entrypoint as described in that package (it has some nice samples) or by using the dotnet template called <code>serverless.AspNetCoreWebAPI</code>, but where I could also use it to map web socket routes, so that the same lambda application works with API Gateway REST and API Gateway Websocket</p>
  <p>I have seen some workarounds like this <a href="https://github.com/aws/aws-lambda-dotnet/issues/644#issuecomment-621528565" rel="nofollow noreferrer">https://github.com/aws/aws-lambda-dotnet/issues/644#issuecomment-621528565</a> where the methods <code>MarshallRequest</code> and <code>PostMarshallRequestFeature</code> are overriden, but I don't fully understand some things such as some class types and that brings me to the following questions:</p>
  <ul>
  <li>Can I have a lambda to serve both integration with REST and WebSocket Api Gateway? Is there any potential issue/recommendation to avoid it?</li>
  <li>How does API Gateway invoke the lambda function and how could I test this functionality? I've had a look at the <a href="https://github.com/aws/aws-lambda-dotnet/tree/master/Tools/LambdaTestTool" rel="nofollow noreferrer">https://github.com/aws/aws-lambda-dotnet/tree/master/Tools/LambdaTestTool</a> and configured it to debug it with rider, but I am absolutely lost when having to test some websocket input. How to do that?</li>
  </ul>
  <p>Ta.</p>
  <hr />
  <p><strong>UPDATE 1</strong> 2021-02-23:</p>
  <p>Thanks. As per the event payload samples here they are:</p>
  <ul>
  <li><p>When <code>$connect</code> with <code>wscat -c wss://{api_id}.execute-api.eu-west-3.amazonaws.com/Prod</code> the APIGatewayProxyRequest is:</p>
  <pre><code>{
      &quot;Resource&quot;: null,
      &quot;Path&quot;: null,
      &quot;HttpMethod&quot;: null,
      &quot;Headers&quot;: {
          &quot;Host&quot;: &quot;xxx.execute-api.eu-west-3.amazonaws.com&quot;,
          &quot;Sec-WebSocket-Extensions&quot;: &quot;permessage-deflate; client_max_window_bits&quot;,
          &quot;Sec-WebSocket-Key&quot;: &quot;xxx==&quot;,
          &quot;Sec-WebSocket-Version&quot;: &quot;13&quot;,
          &quot;X-Amzn-Trace-Id&quot;: &quot;Root=1-6034bfdc-53e605153673d3cb0d0c4177&quot;,
          &quot;X-Forwarded-For&quot;: &quot;185.153.165.122&quot;,
          &quot;X-Forwarded-Port&quot;: &quot;443&quot;,
          &quot;X-Forwarded-Proto&quot;: &quot;https&quot;
      },
      &quot;MultiValueHeaders&quot;: {
          &quot;Host&quot;: [
              &quot;xxx.execute-api.eu-west-3.amazonaws.com&quot;
          ],
          &quot;Sec-WebSocket-Extensions&quot;: [
              &quot;permessage-deflate; client_max_window_bits&quot;
          ],
          &quot;Sec-WebSocket-Key&quot;: [
              &quot;xxx==&quot;
          ],
          &quot;Sec-WebSocket-Version&quot;: [
              &quot;13&quot;
          ],
          &quot;X-Amzn-Trace-Id&quot;: [
              &quot;Root=1-6034bfdc-53e605153673ddcb0d0cxxx&quot;
          ],
          &quot;X-Forwarded-For&quot;: [
              &quot;185.153.165.122&quot;
          ],
          &quot;X-Forwarded-Port&quot;: [
              &quot;443&quot;
          ],
          &quot;X-Forwarded-Proto&quot;: [
              &quot;https&quot;
          ]
      },
      &quot;QueryStringParameters&quot;: null,
      &quot;MultiValueQueryStringParameters&quot;: null,
      &quot;PathParameters&quot;: null,
      &quot;StageVariables&quot;: null,
      &quot;RequestContext&quot;: {
          &quot;Path&quot;: null,
          &quot;AccountId&quot;: null,
          &quot;ResourceId&quot;: null,
          &quot;Stage&quot;: &quot;Prod&quot;,
          &quot;RequestId&quot;: &quot;xxx=&quot;,
          &quot;Identity&quot;: {
              &quot;CognitoIdentityPoolId&quot;: null,
              &quot;AccountId&quot;: null,
              &quot;CognitoIdentityId&quot;: null,
              &quot;Caller&quot;: null,
              &quot;ApiKey&quot;: null,
              &quot;ApiKeyId&quot;: null,
              &quot;AccessKey&quot;: null,
              &quot;SourceIp&quot;: &quot;185.153.165.122&quot;,
              &quot;CognitoAuthenticationType&quot;: null,
              &quot;CognitoAuthenticationProvider&quot;: null,
              &quot;UserArn&quot;: null,
              &quot;UserAgent&quot;: null,
              &quot;User&quot;: null,
              &quot;ClientCert&quot;: null
          },
          &quot;ResourcePath&quot;: null,
          &quot;HttpMethod&quot;: null,
          &quot;ApiId&quot;: &quot;xxx&quot;,
          &quot;ExtendedRequestId&quot;: &quot;xxx=&quot;,
          &quot;ConnectionId&quot;: &quot;xxx=&quot;,
          &quot;ConnectionAt&quot;: 0,
          &quot;DomainName&quot;: &quot;xxx.execute-api.eu-west-3.amazonaws.com&quot;,
          &quot;DomainPrefix&quot;: null,
          &quot;EventType&quot;: &quot;CONNECT&quot;,
          &quot;MessageId&quot;: null,
          &quot;RouteKey&quot;: &quot;$connect&quot;,
          &quot;Authorizer&quot;: null,
          &quot;OperationName&quot;: null,
          &quot;Error&quot;: null,
          &quot;IntegrationLatency&quot;: null,
          &quot;MessageDirection&quot;: &quot;IN&quot;,
          &quot;RequestTime&quot;: &quot;23/Feb/2021:08:42:04 +0000&quot;,
          &quot;RequestTimeEpoch&quot;: 1614069724309,
          &quot;Status&quot;: null
      },
      &quot;Body&quot;: null,
      &quot;IsBase64Encoded&quot;: false
  }
  </code></pre>
  <p>and the APIGatewayProxyResponse is:</p>
  <pre><code>{
      &quot;statusCode&quot;: 200,
      &quot;headers&quot;: null,
      &quot;multiValueHeaders&quot;: null,
      &quot;body&quot;: &quot;Connected.&quot;,
      &quot;isBase64Encoded&quot;: false
  }
  </code></pre>
  </li>
  <li><p>When <code>sendmessage</code> <code>{&quot;message&quot;:&quot;sendmessage&quot;, &quot;data&quot;:&quot;hello world&quot;}</code> the APIGatweayProxyRequest is:</p>
  <pre><code>{
      &quot;Resource&quot;: null,
      &quot;Path&quot;: null,
      &quot;HttpMethod&quot;: null,
      &quot;Headers&quot;: null,
      &quot;MultiValueHeaders&quot;: null,
      &quot;QueryStringParameters&quot;: null,
      &quot;MultiValueQueryStringParameters&quot;: null,
      &quot;PathParameters&quot;: null,
      &quot;StageVariables&quot;: null,
      &quot;RequestContext&quot;: {
          &quot;Path&quot;: null,
          &quot;AccountId&quot;: null,
          &quot;ResourceId&quot;: null,
          &quot;Stage&quot;: &quot;Prod&quot;,
          &quot;RequestId&quot;: &quot;xxx=&quot;,
          &quot;Identity&quot;: {
              &quot;CognitoIdentityPoolId&quot;: null,
              &quot;AccountId&quot;: null,
              &quot;CognitoIdentityId&quot;: null,
              &quot;Caller&quot;: null,
              &quot;ApiKey&quot;: null,
              &quot;ApiKeyId&quot;: null,
              &quot;AccessKey&quot;: null,
              &quot;SourceIp&quot;: &quot;185.153.165.122&quot;,
              &quot;CognitoAuthenticationType&quot;: null,
              &quot;CognitoAuthenticationProvider&quot;: null,
              &quot;UserArn&quot;: null,
              &quot;UserAgent&quot;: null,
              &quot;User&quot;: null,
              &quot;ClientCert&quot;: null
          },
          &quot;ResourcePath&quot;: null,
          &quot;HttpMethod&quot;: null,
          &quot;ApiId&quot;: &quot;xxx&quot;,
          &quot;ExtendedRequestId&quot;: &quot;xxx=&quot;,
          &quot;ConnectionId&quot;: &quot;xxx=&quot;,
          &quot;ConnectionAt&quot;: 0,
          &quot;DomainName&quot;: &quot;4air0zk9w1.execute-api.eu-west-3.amazonaws.com&quot;,
          &quot;DomainPrefix&quot;: null,
          &quot;EventType&quot;: &quot;MESSAGE&quot;,
          &quot;MessageId&quot;: &quot;xxx=&quot;,
          &quot;RouteKey&quot;: &quot;sendmessage&quot;,
          &quot;Authorizer&quot;: null,
          &quot;OperationName&quot;: null,
          &quot;Error&quot;: null,
          &quot;IntegrationLatency&quot;: null,
          &quot;MessageDirection&quot;: &quot;IN&quot;,
          &quot;RequestTime&quot;: &quot;23/Feb/2021:08:42:33 +0000&quot;,
          &quot;RequestTimeEpoch&quot;: 1614069753002,
          &quot;Status&quot;: null
      },
      &quot;Body&quot;: &quot;{\&quot;message\&quot;:\&quot;sendmessage\&quot;, \&quot;data\&quot;:\&quot;hello world\&quot;}&quot;,
      &quot;IsBase64Encoded&quot;: false
  }
  </code></pre>
  <p>and the APIGatewayProxyResponse is:</p>
  <pre><code>{
      &quot;statusCode&quot;: 200,
      &quot;headers&quot;: null,
      &quot;multiValueHeaders&quot;: null,
      &quot;body&quot;: &quot;Data sent to 1 connection&quot;,
      &quot;isBase64Encoded&quot;: false
  }
  </code></pre>
  </li>
  <li><p>When <code>$disconnect</code> the APIGatewayRequest is:</p>
  <pre><code>{
      &quot;Resource&quot;: null,
      &quot;Path&quot;: null,
      &quot;HttpMethod&quot;: null,
      &quot;Headers&quot;: {
          &quot;Host&quot;: &quot;xxx.execute-api.eu-west-3.amazonaws.com&quot;,
          &quot;x-api-key&quot;: &quot;&quot;,
          &quot;X-Forwarded-For&quot;: &quot;&quot;,
          &quot;x-restapi&quot;: &quot;&quot;
      },
      &quot;MultiValueHeaders&quot;: {
          &quot;Host&quot;: [
              &quot;xxx.execute-api.eu-west-3.amazonaws.com&quot;
          ],
          &quot;x-api-key&quot;: [
              &quot;&quot;
          ],
          &quot;X-Forwarded-For&quot;: [
              &quot;&quot;
          ],
          &quot;x-restapi&quot;: [
              &quot;&quot;
          ]
      },
      &quot;QueryStringParameters&quot;: null,
      &quot;MultiValueQueryStringParameters&quot;: null,
      &quot;PathParameters&quot;: null,
      &quot;StageVariables&quot;: null,
      &quot;RequestContext&quot;: {
          &quot;Path&quot;: null,
          &quot;AccountId&quot;: null,
          &quot;ResourceId&quot;: null,
          &quot;Stage&quot;: &quot;Prod&quot;,
          &quot;RequestId&quot;: &quot;xxx=&quot;,
          &quot;Identity&quot;: {
              &quot;CognitoIdentityPoolId&quot;: null,
              &quot;AccountId&quot;: null,
              &quot;CognitoIdentityId&quot;: null,
              &quot;Caller&quot;: null,
              &quot;ApiKey&quot;: null,
              &quot;ApiKeyId&quot;: null,
              &quot;AccessKey&quot;: null,
              &quot;SourceIp&quot;: &quot;185.153.165.122&quot;,
              &quot;CognitoAuthenticationType&quot;: null,
              &quot;CognitoAuthenticationProvider&quot;: null,
              &quot;UserArn&quot;: null,
              &quot;UserAgent&quot;: null,
              &quot;User&quot;: null,
              &quot;ClientCert&quot;: null
          },
          &quot;ResourcePath&quot;: null,
          &quot;HttpMethod&quot;: null,
          &quot;ApiId&quot;: &quot;xxx&quot;,
          &quot;ExtendedRequestId&quot;: &quot;xxxA=&quot;,
          &quot;ConnectionId&quot;: &quot;xxx=&quot;,
          &quot;ConnectionAt&quot;: 0,
          &quot;DomainName&quot;: &quot;xxx.execute-api.eu-west-3.amazonaws.com&quot;,
          &quot;DomainPrefix&quot;: null,
          &quot;EventType&quot;: &quot;DISCONNECT&quot;,
          &quot;MessageId&quot;: null,
          &quot;RouteKey&quot;: &quot;$disconnect&quot;,
          &quot;Authorizer&quot;: null,
          &quot;OperationName&quot;: null,
          &quot;Error&quot;: null,
          &quot;IntegrationLatency&quot;: null,
          &quot;MessageDirection&quot;: &quot;IN&quot;,
          &quot;RequestTime&quot;: &quot;23/Feb/2021:08:42:41 +0000&quot;,
          &quot;RequestTimeEpoch&quot;: 1614069761070,
          &quot;Status&quot;: null
      },
      &quot;Body&quot;: null,
      &quot;IsBase64Encoded&quot;: false
  }
  </code></pre>
  <p>and the APIGatewayProxyResponse is:</p>
  <pre><code>{
      &quot;statusCode&quot;: 200,
      &quot;headers&quot;: null,
      &quot;multiValueHeaders&quot;: null,
      &quot;body&quot;: &quot;Disconnected.&quot;,
      &quot;isBase64Encoded&quot;: false
  }
  </code></pre>
  </li>
  </ul>