Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
7479675
refactor(envelopes): extract abstract base for API Gateway envelopes
j-d-ha Nov 25, 2025
68060fb
feat(options): extend EnvelopeOptions with XML serialization settings
j-d-ha Nov 25, 2025
ede7b26
feat(options): add base64 encoding/decoding options to EnvelopeOptions
j-d-ha Nov 25, 2025
9cefb8c
feat(templates): add GitHub issue and discussion templates (#176)
j-d-ha Nov 25, 2025
b3f3702
feat(core): add ILambdaHostContextAccessor and context factory integr…
j-d-ha Nov 26, 2025
8edd25c
feat(host): add stream feature abstraction layer (#181)
j-d-ha Nov 26, 2025
4f28dac
chore(workflows): pinned release-drafter action to 6.0.0 (#182)
j-d-ha Nov 26, 2025
aef2721
fix(ci): add explicit token to checkout and create-pull-request actio…
j-d-ha Nov 26, 2025
6dcec1c
refactor(options): remove base64 encoding/decoding properties from En…
j-d-ha Nov 26, 2025
8f31c38
refactor(envelopes): make ApiGatewayRequestEnvelope sealed
j-d-ha Nov 26, 2025
4720ea5
refactor(envelopes): apply abstract base class pattern to remaining e…
j-d-ha Nov 26, 2025
38e64d7
refactor(envelopes): adjust formatting of base class declarations
j-d-ha Nov 26, 2025
01133f0
docs(envelopes): add examples for custom XML envelopes
j-d-ha Nov 27, 2025
4eae851
docs(envelopes): add example for custom deserialization in SQS envelopes
j-d-ha Nov 27, 2025
e8448bb
docs(envelopes): add root README and update package-specific document…
j-d-ha Nov 27, 2025
b2bb141
Merge branch 'main' into feature/#175-expand-envelope-options-for-mul…
j-d-ha Nov 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions AwsLambda.Host.sln
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AwsLambda.Host.Example.Events", "examples\AwsLambda.Host.Example.Events\AwsLambda.Host.Example.Events.csproj", "{BA1CA0FB-A0C8-429B-9709-EDEEA4C187B9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Envelopes", "Envelopes", "{1C3C52D9-2936-4A0C-A9C8-F330F22B8359}"
ProjectSection(SolutionItems) = preProject
src\Envelopes\README.md = src\Envelopes\README.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AwsLambda.Host.Envelopes.Sqs", "src\Envelopes\AwsLambda.Host.Envelopes.Sqs\AwsLambda.Host.Envelopes.Sqs.csproj", "{8E3037D3-BEB0-4A0B-A09A-CF31F50D0508}"
EndProject
Expand Down
42 changes: 36 additions & 6 deletions src/AwsLambda.Host.Abstractions/Options/EnvelopeOptions.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,55 @@
using System.Text.Json;
using System.Xml;
using AwsLambda.Host.Envelopes;

namespace AwsLambda.Host.Options;

/// <summary>Options for configuring envelope payload serialization and deserialization.</summary>
/// <remarks>
/// These options are used by implementations of <see cref="IRequestEnvelope" /> and
/// <see cref="IResponseEnvelope" /> to control how Lambda event payloads are processed.
/// </remarks>
/// <seealso cref="IRequestEnvelope" />
/// <seealso cref="IResponseEnvelope" />
public class EnvelopeOptions
{
/// <summary>
/// Gets or sets a dictionary for storing custom extension data associated with envelope
/// processing.
/// </summary>
/// <remarks>
/// <para>
/// This dictionary allows envelope implementations to store and access custom context or
/// configuration data that may be needed during serialization or deserialization.
/// </para>
/// <para>Default is an empty dictionary.</para>
/// </remarks>
public Dictionary<object, object> Items { get; set; } = new();

/// <summary>
/// Gets or sets the JSON serialization options used when extracting and packing Lambda event
/// payloads.
/// </summary>
/// <remarks>
/// <para>
/// These options are passed to implementations of <see cref="IRequestEnvelope" /> and
/// <see cref="IResponseEnvelope" /> to control how payloads are serialized and deserialized.
/// Custom converters, naming policies, and other JSON serialization settings can be configured
/// here.
/// Custom converters, naming policies, and other JSON serialization settings can be
/// configured here.
/// </para>
/// <para>Default is an empty <see cref="JsonSerializerOptions" /> instance.</para>
/// </remarks>
/// <seealso cref="IRequestEnvelope" />
/// <seealso cref="IResponseEnvelope" />
public JsonSerializerOptions JsonOptions { get; set; } = new();

/// <summary>Gets or sets the XML reader settings used when deserializing Lambda event payloads.</summary>
/// <remarks>
/// <para>Options include DTD processing, whitespace handling, and conformance level.</para>
/// <para>Default is a new <see cref="XmlReaderSettings" /> instance.</para>
/// </remarks>
public XmlReaderSettings XmlReaderSettings { get; set; } = new();

/// <summary>Gets or sets the XML writer settings used when serializing Lambda event payloads.</summary>
/// <remarks>
/// <para>Options include indentation, encoding, and XML declaration behavior.</para>
/// <para>Default is a new <see cref="XmlWriterSettings" /> instance.</para>
/// </remarks>
public XmlWriterSettings XmlWriterSettings { get; set; } = new();
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
ο»Ώusing System.Text.Json;
using System.Text.Json.Serialization;
using Amazon.Lambda.APIGatewayEvents;
using AwsLambda.Host.Options;

namespace AwsLambda.Host.Envelopes.ApiGateway;

/// <inheritdoc cref="Amazon.Lambda.APIGatewayEvents.APIGatewayProxyRequest" />
/// <inheritdoc cref="ApiGatewayRequestEnvelopeBase{T}" />
/// <remarks>
/// This class extends <see cref="Amazon.Lambda.APIGatewayEvents.APIGatewayProxyRequest" />
/// and adds a strongly typed <see cref="BodyContent" /> property for easier serialization and
/// deserialization of request payloads.
/// Provides the default implementation for deserializing request payloads using
/// <see cref="System.Text.Json.JsonSerializer" /> with the configured
/// <see cref="EnvelopeOptions.JsonOptions" />.
/// </remarks>
public class ApiGatewayRequestEnvelope<T> : APIGatewayProxyRequest, IRequestEnvelope
public sealed class ApiGatewayRequestEnvelope<T> : ApiGatewayRequestEnvelopeBase<T>
{
/// <summary>The deserialized content of the <see cref="APIGatewayProxyRequest.Body" /></summary>
[JsonIgnore]
public T? BodyContent { get; set; }

/// <inheritdoc />
public void ExtractPayload(EnvelopeOptions options) =>
/// <inheritdoc cref="IRequestEnvelope" />
/// <remarks>This implementation deserializes the request body from JSON.</remarks>
public override void ExtractPayload(EnvelopeOptions options) =>
BodyContent = JsonSerializer.Deserialize<T>(Body, options.JsonOptions);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Text.Json.Serialization;
using Amazon.Lambda.APIGatewayEvents;
using AwsLambda.Host.Options;

namespace AwsLambda.Host.Envelopes.ApiGateway;

/// <inheritdoc cref="Amazon.Lambda.APIGatewayEvents.APIGatewayProxyRequest" />
/// <remarks>
/// This abstract class extends
/// <see cref="Amazon.Lambda.APIGatewayEvents.APIGatewayProxyRequest" /> and provides a foundation
/// for strongly typed request handling. Derived classes implement <see cref="ExtractPayload" /> to
/// deserialize the request body into a strongly typed <see cref="BodyContent" /> property using
/// their chosen deserialization strategy.
/// </remarks>
public abstract class ApiGatewayRequestEnvelopeBase<T> : APIGatewayProxyRequest, IRequestEnvelope
{
/// <summary>The deserialized content of the <see cref="APIGatewayProxyRequest.Body" /></summary>
[JsonIgnore]
public T? BodyContent { get; set; }

/// <inheritdoc />
public abstract void ExtractPayload(EnvelopeOptions options);
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Amazon.Lambda.APIGatewayEvents;
using AwsLambda.Host.Options;

namespace AwsLambda.Host.Envelopes.ApiGateway;

/// <inheritdoc cref="Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse" />
/// <inheritdoc cref="ApiGatewayResponseEnvelopeBase{T}" />
/// <remarks>
/// This class extends <see cref="Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse" />
/// and adds a strongly typed <see cref="BodyContent" /> property for easier serialization and
/// deserialization of response payloads.
/// Provides the default implementation for serializing response payloads using
/// <see cref="System.Text.Json.JsonSerializer" /> with the configured
/// <see cref="EnvelopeOptions.JsonOptions" />.
/// </remarks>
public class ApiGatewayResponseEnvelope<T> : APIGatewayProxyResponse, IResponseEnvelope
public sealed class ApiGatewayResponseEnvelope<T> : ApiGatewayResponseEnvelopeBase<T>
{
/// <summary>The deserialized content of the <see cref="APIGatewayProxyResponse.Body" /></summary>
[JsonIgnore]
public T? BodyContent { get; set; }

/// <inheritdoc />
public void PackPayload(EnvelopeOptions options) =>
/// <inheritdoc cref="IResponseEnvelope" />
/// <remarks>This implementation serializes the response body to JSON.</remarks>
public override void PackPayload(EnvelopeOptions options) =>
Body = JsonSerializer.Serialize(BodyContent, options.JsonOptions);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Text.Json.Serialization;
using Amazon.Lambda.APIGatewayEvents;
using AwsLambda.Host.Options;

namespace AwsLambda.Host.Envelopes.ApiGateway;

/// <inheritdoc cref="Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse" />
/// <remarks>
/// This abstract class extends
/// <see cref="Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse" /> and provides a foundation
/// for strongly typed response handling. Derived classes implement <see cref="PackPayload" /> to
/// serialize the strongly typed <see cref="BodyContent" /> property into the response body using
/// their chosen serialization strategy.
/// </remarks>
public abstract class ApiGatewayResponseEnvelopeBase<T> : APIGatewayProxyResponse, IResponseEnvelope
{
/// <summary>The deserialized content of the <see cref="APIGatewayProxyResponse.Body" /></summary>
[JsonIgnore]
public T? BodyContent { get; set; }

/// <inheritdoc />
public abstract void PackPayload(EnvelopeOptions options);
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
ο»Ώusing System.Text.Json;
using System.Text.Json.Serialization;
using Amazon.Lambda.APIGatewayEvents;
using AwsLambda.Host.Options;

namespace AwsLambda.Host.Envelopes.ApiGateway;

/// <inheritdoc cref="Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyRequest" />
/// <inheritdoc cref="ApiGatewayV2RequestEnvelopeBase{T}" />
/// <remarks>
/// This class extends
/// <see cref="Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyRequest" /> and adds a
/// strongly typed <see cref="BodyContent" /> property for easier serialization and deserialization
/// of request payloads.
/// Provides the default implementation for deserializing request payloads using
/// <see cref="System.Text.Json.JsonSerializer" /> with the configured
/// <see cref="EnvelopeOptions.JsonOptions" />.
/// </remarks>
public class ApiGatewayV2RequestEnvelope<T> : APIGatewayHttpApiV2ProxyRequest, IRequestEnvelope
public sealed class ApiGatewayV2RequestEnvelope<T> : ApiGatewayV2RequestEnvelopeBase<T>
{
/// <summary>The deserialized content of the <see cref="APIGatewayProxyRequest.Body" /></summary>
[JsonIgnore]
public T? BodyContent { get; set; }

/// <inheritdoc />
public void ExtractPayload(EnvelopeOptions options) =>
/// <inheritdoc cref="IRequestEnvelope" />
/// <remarks>This implementation deserializes the request body from JSON.</remarks>
public override void ExtractPayload(EnvelopeOptions options) =>
BodyContent = JsonSerializer.Deserialize<T>(Body, options.JsonOptions);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Text.Json.Serialization;
using Amazon.Lambda.APIGatewayEvents;
using AwsLambda.Host.Options;

namespace AwsLambda.Host.Envelopes.ApiGateway;

/// <inheritdoc cref="Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyRequest" />
/// <remarks>
/// This abstract class extends
/// <see cref="Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyRequest" /> and provides a
/// foundation for strongly typed request handling. Derived classes implement
/// <see cref="ExtractPayload" /> to deserialize the request body into a strongly typed
/// <see cref="BodyContent" /> property using their chosen deserialization strategy.
/// </remarks>
public abstract class ApiGatewayV2RequestEnvelopeBase<T>
: APIGatewayHttpApiV2ProxyRequest,
IRequestEnvelope
{
/// <summary>The deserialized content of the <see cref="APIGatewayHttpApiV2ProxyRequest.Body" /></summary>
[JsonIgnore]
public T? BodyContent { get; set; }

/// <inheritdoc />
public abstract void ExtractPayload(EnvelopeOptions options);
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Amazon.Lambda.APIGatewayEvents;
using AwsLambda.Host.Options;

namespace AwsLambda.Host.Envelopes.ApiGateway;

/// <inheritdoc cref="Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyResponse" />
/// <inheritdoc cref="ApiGatewayV2ResponseEnvelopeBase{T}" />
/// <remarks>
/// This class extends
/// <see cref="Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyResponse" /> and adds a
/// strongly typed <see cref="BodyContent" /> property for easier serialization and deserialization
/// of response payloads.
/// Provides the default implementation for serializing response payloads using
/// <see cref="System.Text.Json.JsonSerializer" /> with the configured
/// <see cref="EnvelopeOptions.JsonOptions" />.
/// </remarks>
public class ApiGatewayV2ResponseEnvelope<T> : APIGatewayHttpApiV2ProxyResponse, IResponseEnvelope
public sealed class ApiGatewayV2ResponseEnvelope<T> : ApiGatewayV2ResponseEnvelopeBase<T>
{
/// <summary>The deserialized content of the <see cref="APIGatewayProxyResponse.Body" /></summary>
[JsonIgnore]
public T? BodyContent { get; set; }

/// <inheritdoc />
public void PackPayload(EnvelopeOptions options) =>
/// <inheritdoc cref="IResponseEnvelope" />
/// <remarks>This implementation serializes the response body to JSON.</remarks>
public override void PackPayload(EnvelopeOptions options) =>
Body = JsonSerializer.Serialize(BodyContent, options.JsonOptions);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Text.Json.Serialization;
using Amazon.Lambda.APIGatewayEvents;
using AwsLambda.Host.Options;

namespace AwsLambda.Host.Envelopes.ApiGateway;

/// <inheritdoc cref="Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyResponse" />
/// <remarks>
/// This abstract class extends
/// <see cref="Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyResponse" /> and provides a
/// foundation for strongly typed response handling. Derived classes implement
/// <see cref="PackPayload" /> to serialize the strongly typed <see cref="BodyContent" /> property
/// into the response body using their chosen serialization strategy.
/// </remarks>
public abstract class ApiGatewayV2ResponseEnvelopeBase<T>
: APIGatewayHttpApiV2ProxyResponse,
IResponseEnvelope
{
/// <summary>The deserialized content of the <see cref="APIGatewayHttpApiV2ProxyResponse.Body" /></summary>
[JsonIgnore]
public T? BodyContent { get; set; }

/// <inheritdoc />
public abstract void PackPayload(EnvelopeOptions options);
}
43 changes: 39 additions & 4 deletions src/Envelopes/AwsLambda.Host.Envelopes.ApiGateway/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,41 @@ internal record Response(string Message, DateTime TimestampUtc);
For HTTP API v2, use `ApiGatewayV2RequestEnvelope<T>` and `ApiGatewayV2ResponseEnvelope<T>` in the
same way.

## Custom Envelopes

To implement custom deserialization logic, extend the appropriate base class and override the
payload handling method:

```csharp
// Example: Custom XML deserialization for requests
public sealed class XmlApiGatewayXmlRequestEnvelope<T> : ApiGatewayRequestEnvelopeBase<T>
{
public override void ExtractPayload(EnvelopeOptions options)
{
using var stringReader = new StringReader(Body);
using var xmlReader = XmlReader.Create(stringReader, options.XmlReaderSettings);
var serializer = new XmlSerializer(typeof(T));
BodyContent = (T)serializer.Deserialize(xmlReader)!;
}
}

// Example: Custom XML serialization for responses
public sealed class XmlApiGatewayXmlResponseEnvelope<T> : ApiGatewayResponseEnvelopeBase<T>
{
public override void PackPayload(EnvelopeOptions options)
{
using var stringWriter = new StringWriter();
using var xmlWriter = XmlWriter.Create(stringWriter, options.XmlWriterSettings);
var serializer = new XmlSerializer(typeof(T));
serializer.Serialize(xmlWriter, BodyContent);
Body = stringWriter.ToString();
}
}
```

This pattern allows you to support multiple serialization formats while maintaining the same
envelope interface.

## AOT Support

When using .NET Native AOT, register all envelope and payload types in your `JsonSerializerContext`:
Expand Down Expand Up @@ -93,11 +128,11 @@ builder.Services.ConfigureEnvelopeOptions(options =>
See the [example project](../../examples/AwsLambda.Host.Example.Events/Program.cs) for a complete
working example.

## Related Resources
## Related Packages

- [Amazon.Lambda.APIGatewayEvents](https://github.com/aws/aws-lambda-dotnet/tree/master/Libraries/src/Amazon.Lambda.APIGatewayEvents) –
Base API Gateway event types
- [AwsLambda.Host](../AwsLambda.Host/README.md) – Core framework documentation
| Package | NuGet | Downloads |
|-------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|
| [**AwsLambda.Host.Envelopes.Sqs**](../AwsLambda.Host.Envelopes.Sqs/README.md) | [![NuGet](https://img.shields.io/nuget/v/AwsLambda.Host.Envelopes.Sqs.svg)](https://www.nuget.org/packages/AwsLambda.Host.Envelopes.Sqs) | [![Downloads](https://img.shields.io/nuget/dt/AwsLambda.Host.Envelopes.Sqs.svg)](https://www.nuget.org/packages/AwsLambda.Host.Envelopes.Sqs/) |

## License

Expand Down
Loading
Loading