Tag: ASP.NET


Subscribing Websites to Amazon SNS Topics

by Norm Johanson | on | in .NET | Permalink | Comments |  Share

Amazon SNS allows you to create topics that have many different subscribers to receive the messages sent from the topic. Amazon SQS queues and emails are probably the most common type of consumers for a topic, but it is also possible to subscribe a website.

Setting Up the Website

The sample application creates a generic handler called SNSReceiver.ashx to handle requests coming from SNS. We’ll discuss each part of the SNSReceiver.ashx individually, but you can download a full copy of SNSReceiver.ashx here.

Each SNS message is sent to the website as an HTTP POST request, which the ProcessRequest method uses to determine if it is is an SNS message that should be processed. For HTTP GET requests, we’ll write back status information of the messages received from SNS.

public void ProcessRequest(HttpContext context)
{
    if(context.Request.HttpMethod == "POST")
    {
        ProcessPost(context);
    }
    else if (context.Request.HttpMethod == "GET")
    {
        ProcessGet(context);
    }
}

SNS messages are sent as JSON documents. In version 2.1.8.0 of the AWS SDK for .NET, we added the utility class Amazon.SimpleNotificationService.Util.Message to parse the JSON. This class also has the ability to verify authenticity of the message coming from SNS. This is done by calling IsMessageSignatureValid. When a subscription is made to a website, the website must confirm the subscription. The confirmation comes into our website like other messages. To detect a confirmation request, we need to check the Type property from the Message object. If the type is SubscriptionConfirmation, then we need to confirm the request; if the type is Notification, then it is a message that needs to be processed.

private void ProcessPost(HttpContext context)
{
    string contentBody;
    using (StreamReader reader = new StreamReader(context.Request.InputStream))
        contentBody = reader.ReadToEnd();

    Message message = Message.ParseMessage(contentBody);

    // Make sure message is authentic
    if (!message.IsMessageSignatureValid())
        throw new Exception("Amazon SNS Message signature is invalid");


    if (message.IsSubscriptionType)
    {
        ConfirmSubscription(context, message);
    }
    else if (message.IsNotificationType)
    {
        ProcessNotification(context, message);
    }
}

To confirm the subscription, we need to call SubscribeToTopic, which uses the SubscribeURL property and makes an HTTP GET request. In a real production situation, you would check the TopicArn property to make sure this is a topic that you should subscribe to.

private void ConfirmSubscription(HttpContext context, Message message)
{
    if (!IsValidTopic(message.TopicArn))
        return;

    try
    {
        message.SubscribeToTopic();
        Trace.WriteLine(string.Format("Subscription to {0} confirmed.", message.TopicArn));
    }
    catch(Exception e)
    {
        Trace.WriteLine(string.Format("Error confirming subscription to {0}: {1}", message.TopicArn, e.Message));
    }
}

To process messages, we grab the MessageText property from the Message object. For demonstration purposes, we add each message to a list of messages that we attach to the Application object. This list of messages is then returned by the GET request handler to display the list of messages received.

private void ProcessNotification(HttpContext context, Message message)
{
    var log = context.Application["log"] as IList;
    if (log == null)
    {
        log = new List();
        context.Application["log"] = log;
    }

    log.Add(string.Format("{0}: Received notification from {1} with message {2}", DateTime.Now, message.TopicArn, message.MessageText));
}

Here is the ProcessGet method that called from ProcessRequest for HTTP GET requests. It shows the list of received messages from SNS.

private void ProcessGet(HttpContext context)
{
    context.Response.ContentType = "text/plain";
    var log = context.Application["log"] as IList;
    if (log == null)
    {
        context.Response.Write("No log messages");
    }
    else
    {
        foreach (var message in log.Reverse())
        {
            context.Response.Write(message + "n");
        }
    }
}

Setting Up a Subscription

Remember that our website must be publicly accessible for SNS to send messages to it. We tested this by first deploying the application to AWS using AWS Elastic Beanstalk. We can use either the AWS Management Console or the AWS Toolkit for Visual Studio. Let’s use the Toolkit to test this.

First, we need to create the topic. In AWS Explorer, right-click Amazon SNS and select Create Topic, give the topic a name, and click OK.

Double-click on the new topic in the explorer to bring up its view. Click Create New Subscription, select HTTP or HTTPS for the protocol depending on how you deployed your application, and specify the URL to the SNSReceiver.ashx.

Depending on how fast the site responds to the confirmation, you might see a subscription status of "Pending Confirmation". If that’s the case, then just click the refresh button.

Once the subscription is confirmed, we can a send a test message by clicking the Publish to Topic button, adding some sample text, and clicking OK. Since our website will respond to GET requests by writing out the messages it receives, we can navigate to the website to see if the test message made it.

Now that we have confirmed messages are being sent and received by our website, we can use the AWS SDK for .NET or any of the other AWS SDKs to send messages to our website. Here is a snippet of how to use the .NET SDK to send messages.

var snsClient = new AmazonSimpleNotificationServiceClient(RegionEndpoint.USWest2);
snsClient.Publish(new PublishRequest
{
    TopicArn = topicArn,
    Message = "Test message"
});

Enjoy sending messages, and let us know what you think.