

The AWS SDK for .NET V3 has entered maintenance mode.

We recommend that you migrate to [AWS SDK for .NET V4](https://docs.aws.amazon.com/sdk-for-net/v4/developer-guide/welcome.html). For additional details and information on how to migrate, please refer to our [maintenance mode announcement](https://aws.amazon.com/blogs/developer/aws-sdk-for-net-v3-maintenance-mode-announcement/).

# AWS Message Processing Framework for .NET
<a name="msg-proc-fw"></a>

The AWS Message Processing Framework for .NET is an AWS-native framework that simplifies the development of .NET message processing applications that use AWS services such as Amazon Simple Queue Service (SQS), Amazon Simple Notification Service (SNS), and Amazon EventBridge. The framework reduces the amount of boiler-plate code developers need to write, allowing you to focus on your business logic when publishing and consuming messages. For details about how the framework can simplify your development, see the blog post [Introducing the AWS Message Processing Framework for .NET (Preview)](https://aws.amazon.com/blogs/developer/introducing-the-aws-message-processing-framework-for-net-preview/). The first part in particular provides a demonstration that shows the difference between using low-level API calls and using the framework.

The Message Processing Framework supports the following activities and features:
+ Sending messages to SQS and publishing events to SNS and EventBridge.
+ Receiving and handling messages from SQS by using a long-running poller, which is typically used in background services. This includes managing the visibility timeout while a message is being handled to prevent other clients from processing it.
+ Handling messages in AWS Lambda functions.
+ FIFO (first-in-first-out) SQS queues and SNS topics.
+ OpenTelemetry for logging.

For details about these activities and features see the **Features** section of the [blog post](https://aws.amazon.com/blogs/developer/introducing-the-aws-message-processing-framework-for-net-preview/) and the topics listed below.

Before you begin, be sure you have [set up your environment and project](net-dg-config.md). Also review the information in [SDK features](net-dg-sdk-features.md).

**Additional resources**
+ The [https://www.nuget.org/packages/AWS.Messaging/](https://www.nuget.org/packages/AWS.Messaging/) package on [NuGet.org](https://www.nuget.org/).
+ The [API reference](https://aws.github.io/aws-dotnet-messaging/).
+ The `README` file in the GitHub repo at [https://github.com/aws/aws-dotnet-messaging/](https://github.com/aws/aws-dotnet-messaging/)
+ [.NET dependency injection](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection) from Microsoft.
+ [.NET Generic Host](https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host) from Microsoft.

**Topics**
+ [Get started](msg-proc-fw-get-started.md)
+ [Publish messages](msg-proc-fw-publish.md)
+ [Consume messages](msg-proc-fw-consume.md)
+ [FIFO](msg-proc-fw-fifo.md)
+ [Logging and Open Telemetry](msg-proc-fw-telemetry.md)
+ [Customize](msg-proc-fw-customize.md)
+ [Security](msg-proc-fw-security.md)

# Get started with the AWS Message Processing Framework for .NET
<a name="msg-proc-fw-get-started"></a>

Before you begin, be sure you have [set up your environment and project](net-dg-config.md). Also review the information in [SDK features](net-dg-sdk-features.md).

This topic provides information that will help you get started using the Message Processing Framework. In addition to prerequisite and configuration information, a tutorial is provided that shows you how to implement a common scenario.

## Prerequisites and configuration
<a name="mpf-get-started-prereq"></a>
+ The credentials you provide for your application must have appropriate permissions for the messaging service and operations that it uses. For more information, see the security topics for [SQS](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-authentication-and-access-control.html), [SNS](https://docs.aws.amazon.com/sns/latest/dg/security-iam.html), and [EventBridge](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-iam.html) in their respective developer guides. Also see the portion of the [README](https://github.com/aws/aws-dotnet-messaging/) file on GitHub that discusses specific [permissions](https://github.com/aws/aws-dotnet-messaging/blob/main/README.md#permissions).
+ To use the AWS Message Processing Framework for .NET, you must add the [https://www.nuget.org/packages/AWS.Messaging](https://www.nuget.org/packages/AWS.Messaging) NuGet package to your project. For example:

  ```
  dotnet add package AWS.Messaging
  ```
+ The framework integrates with .NET's [dependency injection (DI) service container](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection). You can configure the framework during your application's startup by calling `AddAWSMessageBus` to add it to the DI container.

  ```
  var builder = WebApplication.CreateBuilder(args);
  
  // Register the AWS Message Processing Framework for .NET
  builder.Services.AddAWSMessageBus(builder =>
  {
      // Register that you'll publish messages of type ChatMessage to an existing queue
      builder.AddSQSPublisher<ChatMessage>("https://sqs.us-west-2.amazonaws.com/012345678910/MyAppProd");
  });
  ```

## Tutorial
<a name="mpf-get-started-tutorial"></a>

This tutorial demonstrates how to use the AWS Message Processing Framework for .NET. It creates two applications: an ASP.NET Core Minimal API that sends messages to an Amazon SQS queue when it receives a request at an API endpoint, and a long-running console application that polls for these messages and handles them. 
+ The instructions in this tutorial favor the .NET CLI, but you can perform this tutorial by using either cross-platform tools such as the .NET CLI or Microsoft Visual Studio. For information about tools, see [Install and configure your toolchain](net-dg-dev-env.md).
+ This tutorial assumes that you're using your `[default]` profile for credentials. It also assumes that short-term credentials are available with appropriate permissions for sending and receiving Amazon SQS messages. For more information, see [Configure SDK authentication with AWS](creds-idc.md) and the security topics for [SQS](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-authentication-and-access-control.html).

**Note**  
By running this tutorial, you might incur costs for SQS messaging.

### Steps
<a name="mpf-tutorial-steps"></a>
+ [Create an SQS queue](#mpf-tutorial-queue)
+ [Create and run the publishing application](#mpf-tutorial-publish)
+ [Create and run the handling application](#mpf-tutorial-handle)
+ [Cleanup](#mpf-tutorial-cleanup)

### Create an SQS queue
<a name="mpf-tutorial-queue"></a>

This tutorial requires an SQS queue to send messages to and receive messages from. A queue can be created by using one of the following commands for the AWS CLI or the AWS Tools for PowerShell. Take note of the queue URL that is returned so that you can specify it in the framework configuration that follows.

------
#### [ AWS CLI ]

```
aws sqs create-queue --queue-name DemoQueue
```

------
#### [ AWS Tools for PowerShell ]

```
New-SQSQueue -QueueName DemoQueue
```

------

### Create and run the publishing application
<a name="mpf-tutorial-publish"></a>

Use the following procedure to create and run the publishing application.

1. Open a command prompt or terminal. Find or create an operating system folder under which you can create a .NET project.

1. In that folder, run the following command to create the .NET project.

   ```
   dotnet new webapi --name Publisher
   ```

1. Navigate into the new project's folder. Add a dependency on the AWS Message Processing Framework for .NET.

   ```
   cd Publisher
   dotnet add package AWS.Messaging
   ```
**Note**  
If you're using AWS IAM Identity Center for authentication, be sure to also add `AWSSDK.SSO` and `AWSSDK.SSOOIDC`.

1. Replace the code in `Program.cs` with the following code.

   ```
   using AWS.Messaging;
   using Microsoft.AspNetCore.Mvc;
   using Publisher;
   
   var builder = WebApplication.CreateBuilder(args);
   
   // Add services to the container.
   // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle.
   builder.Services.AddEndpointsApiExplorer();
   builder.Services.AddSwaggerGen();
   
   
   // Configure the AWS Message Processing Framework for .NET.
   builder.Services.AddAWSMessageBus(builder =>
   {
       // Check for input SQS URL.
       // The SQS URL should be passed as a command line argument or set in the Debug launch profile.
       if ((args.Length == 1) && (args[0].Contains("https://sqs.")))
       {
           // Register that you'll publish messages of type GreetingMessage:
           // 1. To a specified queue.
           // 2. Using the message identifier "greetingMessage", which will be used
           //    by handlers to route the message to the appropriate handler.
           builder.AddSQSPublisher<GreetingMessage>(args[0], "greetingMessage");
       }
       // You can map additional message types to queues or topics here as well.
   });
   var app = builder.Build();
   
   
   // Configure the HTTP request pipeline.
   if (app.Environment.IsDevelopment())
   {
       app.UseSwagger();
       app.UseSwaggerUI();
   }
   
   app.UseHttpsRedirection();
   
   // Create an API Endpoint that receives GreetingMessage objects
   // from the caller and then sends them as an SQS message.
   app.MapPost("/greeting", async ([FromServices] IMessagePublisher publisher, Publisher.GreetingMessage message) =>
       {
           return await PostGreeting(message, publisher);
       })
   .WithName("SendGreeting")
   .WithOpenApi();
   
   app.Run();
   
   public partial class Program
   {
       /// <summary>
       /// Endpoint for posting a greeting message.
       /// </summary>
       /// <param name="greetingMessage">The greeting message.</param>
       /// <param name="messagePublisher">The message publisher.</param>
       /// <returns>Async task result.</returns>
       public static async Task<IResult> PostGreeting(GreetingMessage greetingMessage,
           IMessagePublisher messagePublisher)
       {
           if (greetingMessage.SenderName == null || greetingMessage.Greeting == null)
           {
               return Results.BadRequest();
           }
   
           // Publish the message to the queue configured above.
           await messagePublisher.PublishAsync(greetingMessage);
   
           return Results.Ok();
       }
   }
   
   namespace Publisher
   {
       /// <summary>
       /// This class represents the message contents.
       /// </summary>
       public class GreetingMessage
       {
           public string? SenderName { get; set; }
           public string? Greeting { get; set; }
       }
   }
   ```

1. Run the following command. This should open a browser window with the Swagger UI, which allows you to explore and test your API.

   ```
   dotnet watch run <queue URL created earlier>
   ```

1. Open the `/greeting` endpoint and choose **Try it out**.

1. Specify `senderName` and `greeting` values for the message, and choose **Execute**. This invokes your API, which sends the SQS message.

### Create and run the handling application
<a name="mpf-tutorial-handle"></a>

Use the following procedure to create and run the handling application.

1. Open a command prompt or terminal. Find or create an operating system folder under which you can create a .NET project.

1. In that folder, run the following command to create the .NET project.

   ```
   dotnet new console --name Handler
   ```

1. Navigate into the new project's folder. Add a dependency on the AWS Message Processing Framework for .NET. Also add the `Microsoft.Extensions.Hosting` package, which allows you to configure the framework through the [.NET Generic Host](https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host).

   ```
   cd Handler
   dotnet add package AWS.Messaging
   dotnet add package Microsoft.Extensions.Hosting
   ```
**Note**  
If you're using AWS IAM Identity Center for authentication, be sure to also add `AWSSDK.SSO` and `AWSSDK.SSOOIDC`.

1. Replace the code in `Program.cs` with the following code.

   ```
   using AWS.Messaging;
   using Handler;
   using Microsoft.Extensions.DependencyInjection;
   using Microsoft.Extensions.Hosting;
   
   var builder = Host.CreateDefaultBuilder(args);
   
   builder.ConfigureServices(services =>
   {
       // Register the AWS Message Processing Framework for .NET.
       services.AddAWSMessageBus(builder =>
       {
           // Check for input SQS URL.
           // The SQS URL should be passed as a command line argument or set in the Debug launch profile.
           if ((args.Length == 1) && (args[0].Contains("https://sqs.")))
           {
               // Register you'll poll the following queue.
               builder.AddSQSPoller(args[0]);
   
               // And that messages of type "greetingMessage" should be:
               // 1. Deserialized as GreetingMessage objects.
               // 2. Which are then passed to GreetingMessageHandler.
               builder.AddMessageHandler<GreetingMessageHandler, GreetingMessage>("greetingMessage");
   
           }
           // You can add additional message handlers here, using different message types. 
       });
   });
   
   var host = builder.Build();
   await host.RunAsync();
   
   namespace Handler
   {
       /// <summary>
       /// This class represents the message contents.
       /// </summary>
       public class GreetingMessage
       {
           public string? SenderName { get; set; }
           public string? Greeting { get; set; }
       }
   
       /// <summary>
       /// This handler is invoked each time you receive the message.
       /// </summary>
       public class GreetingMessageHandler : IMessageHandler<GreetingMessage>
       {
           public Task<MessageProcessStatus> HandleAsync(
               MessageEnvelope<GreetingMessage> messageEnvelope,
               CancellationToken token = default)
           {
               Console.WriteLine(
                   $"Received message {messageEnvelope.Message.Greeting} from {messageEnvelope.Message.SenderName}");
               return Task.FromResult(MessageProcessStatus.Success());
           }
       }
   }
   ```

1. Run the following command. This starts a long-running poller.

   ```
   dotnet run <queue URL created earlier>
   ```

   Shortly after startup the application will receive the message that was sent in the first part of this tutorial and log the following message:

   ```
   Received message {greeting} from {senderName}
   ```

1. Press `Ctrl+C` to stop the poller.

### Cleanup
<a name="mpf-tutorial-cleanup"></a>

Use one of the following commands for the AWS CLI or the AWS Tools for PowerShell to delete the queue.

------
#### [ AWS CLI ]

```
aws sqs delete-queue --queue-url "<queue URL created earlier>"
```

------
#### [ AWS Tools for PowerShell ]

```
Remove-SQSQueue -QueueUrl "<queue URL created earlier>"
```

------

# Publish messages with the AWS Message Processing Framework for .NET
<a name="msg-proc-fw-publish"></a>

The AWS Message Processing Framework for .NET supports publishing one or more message types, processing one or more message types, or doing both in the same application. 

The following code shows a configuration for an application that is publishing different message types to different AWS services. 

```
var builder = WebApplication.CreateBuilder(args);

// Register the AWS Message Processing Framework for .NET
builder.Services.AddAWSMessageBus(builder =>
{
    // Register that you'll send messages of type ChatMessage to an existing queue
    builder.AddSQSPublisher<ChatMessage>("https://sqs.us-west-2.amazonaws.com/012345678910/MyAppProd");

    // Register that you'll publish messages of type OrderInfo to an existing SNS topic
    builder.AddSNSPublisher<OrderInfo>("arn:aws:sns:us-west-2:012345678910:MyAppProd");

    // Register that you'll publish messages of type FoodItem to an existing EventBridge bus
    builder.AddEventBridgePublisher<FoodItem>("arn:aws:events:us-west-2:012345678910:event-bus/default");
});
```

Once you have registered the framework during startup, inject the generic `IMessagePublisher` into your code. Call its `PublishAsync` method to publish any of the message types that were configured above. The generic publisher will determine the destination to route the message to based on its type. 

 In the following example, an ASP.NET MVC controller receives both `ChatMessage` messages and `OrderInfo` events from users, and then publishes them to Amazon SQS and Amazon SNS respectively. Both message types can be published using the generic publisher that was configured above.

```
[ApiController]
[Route("[controller]")]
public class PublisherController : ControllerBase
{
    private readonly IMessagePublisher _messagePublisher;

    public PublisherController(IMessagePublisher messagePublisher)
    {
        _messagePublisher = messagePublisher;
    }

    [HttpPost("chatmessage", Name = "Chat Message")]
    public async Task<IActionResult> PublishChatMessage([FromBody] ChatMessage message)
    {
        // Perform business and validation logic on the ChatMessage here.
        if (message == null)
        {
            return BadRequest("A chat message was not submitted. Unable to forward to the message queue.");
        }
        if (string.IsNullOrEmpty(message.MessageDescription))
        {
            return BadRequest("The MessageDescription cannot be null or empty.");
        }

        // Send the ChatMessage to SQS, using the generic publisher.
        await _messagePublisher.PublishAsync(message);

        return Ok();
    }

    [HttpPost("order", Name = "Order")]
    public async Task<IActionResult> PublishOrder([FromBody] OrderInfo message)
    {
        if (message == null)
        {
            return BadRequest("An order was not submitted.");
        }

        // Publish the OrderInfo to SNS, using the generic publisher.
        await _messagePublisher.PublishAsync(message);

        return Ok();
    }
}
```

In order to route a message to the appropriate handling logic, the framework uses metadata called the *message type identifier*. By default, this is the full name of the message's .NET type, including its assembly name. If you're both sending and handling messages, this mechanism works well if you share the definition of your message objects across projects. However, if the messages are redefined in different namespaces, or if you're exchanging messages with other frameworks or programming languages, then you might need to override the message type identifier.

```
var builder = Host.CreateDefaultBuilder(args);

builder.ConfigureServices(services =>
{
    // Register the AWS Message Processing Framework for .NET
    services.AddAWSMessageBus(builder =>
    {
        // Register that you'll publish messages of type GreetingMessage to an existing queue
        builder.AddSQSPublisher<GreetingMessage>("https://sqs.us-west-2.amazonaws.com/012345678910/MyAppProd", "greetingMessage");
    });
});
```

## Service-specific publishers
<a name="service-specific-publishers"></a>

The example shown above uses the generic `IMessagePublisher`, which can publish to any supported AWS service based on the configured message type. The framework also provides service-specific publishers for Amazon SQS, Amazon SNS and Amazon EventBridge. These specific publishers expose options that only apply to that service, and can be injected using the types `ISQSPublisher`, `ISNSPublisher`, and `IEventBridgePublisher`.

For example, when sending messages to an SQS FIFO queue, you must set the appropriate [message group ID](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/FIFO-key-terms.html). The following code shows the `ChatMessage` example again, but now using an `ISQSPublisher` to set SQS-specific options.

```
public class PublisherController : ControllerBase
{
    private readonly ISQSPublisher _sqsPublisher;

    public PublisherController(ISQSPublisher sqsPublisher)
    {
        _sqsPublisher = sqsPublisher;
    }

    [HttpPost("chatmessage", Name = "Chat Message")]
    public async Task<IActionResult> PublishChatMessage([FromBody] ChatMessage message)
    {
        // Perform business and validation logic on the ChatMessage here
        if (message == null)
        {
            return BadRequest("A chat message was not submitted. Unable to forward to the message queue.");
        }
        if (string.IsNullOrEmpty(message.MessageDescription))
        {
            return BadRequest("The MessageDescription cannot be null or empty.");
        }

        // Send the ChatMessage to SQS using the injected ISQSPublisher, with SQS-specific options
        await _sqsPublisher.SendAsync(message, new SQSOptions
        {
            DelaySeconds = <delay-in-seconds>,
            MessageAttributes = <message-attributes>,
            MessageDeduplicationId = <message-deduplication-id>,
            MessageGroupId = <message-group-id>
        });

        return Ok();
    }
}
```

The same can be done for SNS and EventBridge, using `ISNSPublisher` and `IEventBridgePublisher` respectively.

```
await _snsPublisher.PublishAsync(message, new SNSOptions
{
    Subject = <subject>,
    MessageAttributes = <message-attributes>,
    MessageDeduplicationId = <message-deduplication-id>,
    MessageGroupId = <message-group-id>
});
```

```
await _eventBridgePublisher.PublishAsync(message, new EventBridgeOptions
{
    DetailType = <detail-type>,
    Resources = <resources>,
    Source = <source>,
    Time = <time>,
    TraceHeader = <trace-header>
});
```

By default, messages of a given type are sent to the destination that is configured in advance. However, you can override the destination for a single message using the message-specific publishers. You can also override the underlying AWS SDK for .NET client that is used to publish the message, which can be useful in multi-tenant applications where you need to change roles or credentials, depending on the destination.

```
await _sqsPublisher.SendAsync(message, new SQSOptions
{
    OverrideClient = <override IAmazonSQS client>,
    QueueUrl = <override queue URL>
});
```

# Consume messages with the AWS Message Processing Framework for .NET
<a name="msg-proc-fw-consume"></a>

The AWS Message Processing Framework for .NET allows you to consume messages that have been [published](msg-proc-fw-publish.md) by using the framework or one of the messaging services. The messages can be consumed in a variety of ways, some of which are described below.

## Message Handlers
<a name="handle-a-message"></a>

To consume messages, implement a message handler using the `IMessageHandler` interface for each message type you wish to process. The mapping between message types and message handlers is configured in the project startup.

```
await Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        // Register the AWS Message Processing Framework for .NET
        services.AddAWSMessageBus(builder =>
        {
            // Register an SQS Queue that the framework will poll for messages.
            // NOTE: The URL given below is an example. Use the appropriate URL for your SQS Queue.
            builder.AddSQSPoller("https://sqs.us-west-2.amazonaws.com/012345678910/MyAppProd");

            // Register all IMessageHandler implementations with the message type they should process. 
            // Here messages that match our ChatMessage .NET type will be handled by our ChatMessageHandler
            builder.AddMessageHandler<ChatMessageHandler, ChatMessage>();
        });
    })
    .Build()
    .RunAsync();
```

The following code shows a sample message handler for a `ChatMessage` message. 

```
public class ChatMessageHandler : IMessageHandler<ChatMessage>
{
    public Task<MessageProcessStatus> HandleAsync(MessageEnvelope<ChatMessage> messageEnvelope, CancellationToken token = default)
    {
        // Add business and validation logic here.
        if (messageEnvelope == null)
        {
            return Task.FromResult(MessageProcessStatus.Failed());
        }

        if (messageEnvelope.Message == null)
        {
            return Task.FromResult(MessageProcessStatus.Failed());
        }

        ChatMessage message = messageEnvelope.Message;

        Console.WriteLine($"Message Description: {message.MessageDescription}");

        // Return success so the framework will delete the message from the queue.
        return Task.FromResult(MessageProcessStatus.Success());
    }
}
```

The outer `MessageEnvelope` contains metadata used by the framework. Its `message` property is the message type (in this case `ChatMessage`).

You can return `MessageProcessStatus.Success()` to indicate that the message was processed successfully and the framework will delete the message from the Amazon SQS queue. When returning `MessageProcessStatus.Failed()`, the message will remain in the queue where it can be processed again or moved to a [dead-letter queue](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html), if configured.

## Handling Messages in a Long-Running Process
<a name="long-running-process"></a>

You can call `AddSQSPoller` with an SQS queue URL to start a long-running [https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.backgroundservice](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.backgroundservice) that will continuously poll the queue and process messages.

```
await Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        // Register the AWS Message Processing Framework for .NET
        services.AddAWSMessageBus(builder =>
        {
            // Register an SQS Queue that the framework will poll for messages.
            // NOTE: The URL given below is an example. Use the appropriate URL for your SQS Queue.
            builder.AddSQSPoller("https://sqs.us-west-2.amazonaws.com/012345678910/MyAppProd", options => 
            {
                // The maximum number of messages from this queue that the framework will process concurrently on this client.
                options.MaxNumberOfConcurrentMessages = 10;

                // The duration each call to SQS will wait for new messages.
                options.WaitTimeSeconds = 20; 
            });

            // Register all IMessageHandler implementations with the message type they should process.
            builder.AddMessageHandler<ChatMessageHandler, ChatMessage>();
        });
    })
    .Build()
    .RunAsync();
```

### Configuring the SQS Message Poller
<a name="config-msg-poller"></a>

The SQS message poller can be configured by the `SQSMessagePollerOptions` when calling `AddSQSPoller`.
+ `MaxNumberOfConcurrentMessages` - The maximum number of messages from the queue to process concurrently. The default value is 10.
+ `WaitTimeSeconds` - The duration (in seconds) for which the `ReceiveMessage` SQS call waits for a message to arrive in the queue before returning. If a message is available, the call returns sooner than `WaitTimeSeconds`. The default value is 20.

**Message Visibility Timeout Handling**

SQS messages have a [visibility timeout](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html) period. When one consumer begins handling a given message, it remains in the queue but is hidden from other consumers to avoid processing it more than once. If the message is not handled and deleted before becoming visible again, another consumer might attempt to handle the same message.

The framework will track and attempt to extend the visibility timeout for messages that it is currently handling. You can configure this behavior on the `SQSMessagePollerOptions` when calling `AddSQSPoller`.
+ `VisibilityTimeout` - The duration in seconds that received messages are hidden from subsequent retrieve requests. The default value is 30.
+ `VisibilityTimeoutExtensionThreshold` - When a message's visibility timeout is within this many seconds of expiring, the framework will extend the visibility timeout (by another `VisibilityTimeout` seconds). The default value is 5.
+ `VisibilityTimeoutExtensionHeartbeatInterval` - How often in seconds that the framework will check for messages that are within `VisibilityTimeoutExtensionThreshold` seconds of expiring, and then extend their visibility timeout. The default value is 1.

 In the following example, the framework will check every 1 second for messages that are still being handled. For those messages within 5 seconds of becoming visible again, the framework will automatically extend the visibility timeout of each message by another 30 seconds.

```
// NOTE: The URL given below is an example. Use the appropriate URL for your SQS Queue.
builder.AddSQSPoller("https://sqs.us-west-2.amazonaws.com/012345678910/MyAppProd", options => 
{
    options.VisibilityTimeout = 30;
    options.VisibilityTimeoutExtensionThreshold = 5;
    VisibilityTimeoutExtensionHeartbeatInterval = 1;
});
```

## Handling messages in AWS Lambda functions
<a name="lambda-functions"></a>

You can use the AWS Message Processing Framework for .NET with [SQS's integration with Lambda](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html). This is provided by the `AWS.Messaging.Lambda` package. Refer to its [README](https://github.com/aws/aws-dotnet-messaging/blob/main/src/AWS.Messaging.Lambda/README.md) to get started.

# Using FIFO with the AWS Message Processing Framework for .NET
<a name="msg-proc-fw-fifo"></a>

For use cases where message ordering and message deduplication are critical, the AWS Message Processing Framework for .NET supports first-in-first-out (FIFO) [Amazon SQS queues](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-fifo-queues.html) and [Amazon SNS topics](https://docs.aws.amazon.com/sns/latest/dg/sns-fifo-topics.html).

## Publishing
<a name="mpf-fifo-publish"></a>

When publishing messages to a FIFO queue or topic, you must set the message group ID, which specifies the group that the message belongs to. Messages within a group are processed in order. You can set this on the SQS-specific and SNS-specific message publishers.

```
await _sqsPublisher.PublishAsync(message, new SQSOptions
{
    MessageDeduplicationId = <message-deduplication-id>,
    MessageGroupId = <message-group-id>
});
```

## Subscribing
<a name="mpf-fifo-subscribe"></a>

When handling messages from a FIFO queue, the framework handles messages within a given message group in the order in which they were received for each `ReceiveMessages` call. The framework enters this mode of operation automatically when configured with a queue ending in `.fifo`.

```
await Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        // Register the AWS Message Processing Framework for .NET.
        services.AddAWSMessageBus(builder =>
        {
            // Because this is a FIFO queue, the framework automatically handles these messages in order.
            builder.AddSQSPoller("https://sqs.us-west-2.amazonaws.com/012345678910/MPF.fifo");
            builder.AddMessageHandler<OrderMessageHandler, OrderMessage>();
        });
    })
    .Build()
    .RunAsync();
```

# Logging and Open Telemetry for the AWS Message Processing Framework for .NET
<a name="msg-proc-fw-telemetry"></a>

The AWS Message Processing Framework for .NET is instrumented for OpenTelemetry to log [traces](https://opentelemetry.io/docs/concepts/signals/traces/) for each message that is published or handled by the framework. This is provided by the [https://www.nuget.org/packages/AWS.Messaging.Telemetry.OpenTelemetry](https://www.nuget.org/packages/AWS.Messaging.Telemetry.OpenTelemetry) package. Refer to its [README](https://github.com/aws/aws-dotnet-messaging/blob/main/src/AWS.Messaging.Telemetry.OpenTelemetry/README.md) to get started.

**Note**  
For security information related to logging, see [Security for the AWS Message Processing Framework for .NET](msg-proc-fw-security.md).

# Customize the AWS Message Processing Framework for .NET
<a name="msg-proc-fw-customize"></a>

The AWS Message Processing Framework for .NET builds, sends, and handles messages in three different "layers":

1. At the outermost layer, the framework builds the AWS-native request or response specific to a service. With Amazon SQS for example, it builds [https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html) requests, and works with the [https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_Message.html](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_Message.html) objects that are defined by the service.

1. Inside the SQS request and response, the framework sets the `MessageBody` element (or `Message` for Amazon SNS or `Detail` for Amazon EventBridge) to a [JSON-formatted CloudEvent](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/formats/json-format.md). This contains metadata set by the framework that is accessible on the `MessageEnvelope` object when handling a message.

1. At the innermost layer, the `data` attribute inside the CloudEvent JSON object contains a JSON serialization of the .NET object that was sent or received as the message.

   ```
   {
       "id":"b02f156b-0f02-48cf-ae54-4fbbe05cffba",
       "source":"/aws/messaging",
       "specversion":"1.0",
       "type":"Publisher.Models.ChatMessage",
       "time":"2023-11-21T16:36:02.8957126+00:00",
       "data":"<the ChatMessage object serialized as JSON>"
   }
   ```

You can customize how the message envelope is configured and read:
+ `"id"` uniquely identifies the message. By default it is set to a new GUID, but this can be overridden by implementing your own `IMessageIdGenerator` and injecting that into the DI container. 
+ `"type"` controls how the message is routed to handlers. By default this uses the full name of the .NET type that corresponds to the message. You can override this via the `messageTypeIdentifier` parameter when mapping the message type to the destination via `AddSQSPublisher`, `AddSNSPublisher`, or `AddEventBridgePublisher`.
+ `"source"` indicates which system or server sent the message.
  + This will be the function name if publishing from AWS Lambda, the cluster name and task ARN if on Amazon ECS, the instance ID if on Amazon EC2, otherwise a fallback value of `/aws/messaging`. 
  + You can override this via `AddMessageSource` or `AddMessageSourceSuffix` on the `MessageBusBuilder`.
+ `"time"` set to the current DateTime in UTC. This can be overridden by implementing your own `IDateTimeHandler` and injecting that into the DI container.
+ `"data"` contains a JSON representation of the .NET object that was sent or received as the message:
  + `ConfigureSerializationOptions` on `MessageBusBuilder` allows you to configure the [https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions](https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions) that will be used when serializing and deserializing the message.
  + To inject additional attributes or transform the message envelope once the framework builds it, you can implement `ISerializationCallback` and register that via `AddSerializationCallback` on `MessageBusBuilder`.

# Security for the AWS Message Processing Framework for .NET
<a name="msg-proc-fw-security"></a>

The AWS Message Processing Framework for .NET relies on the AWS SDK for .NET for communicating with AWS. For more information about security in the AWS SDK for .NET, see [Security for this AWS Product or Service](security.md).

For security purposes, the framework doesn't log data messages sent by the user. If you want to enable this functionality for debugging purposes, you need to call `EnableDataMessageLogging()` in the Message Bus as follows:

```
builder.Services.AddAWSMessageBus(bus =>
{
    builder.EnableDataMessageLogging();
});
```

If you discover a potential security issue, refer to the [security policy](https://github.com/aws/aws-dotnet-messaging/security/policy) for reporting information.