Moving from HTTP-Triggered Azure Functions to Web API (Update 2019.07.09)

Fireworks over Chicago

This past weekend was a long one due to the Fourth of July, and despite a weekend filled with cookouts, swimming, fireworks, an anniversary date night, and a trip to St. Louis, I was able to knock off an important task on my side project‘s TODO List.

Refactoring HTTP-Triggered Azure Functions into a Web API Service

When I started work on Stock Alerts, I began with the Azure Functions for retrieving quotes and evaluating alert definitions, because they were the most interesting pieces to me.

I then started thinking about the API endpoints that I’d need to support a mobile client. I figured I wouldn’t need too many endpoints to support the very minimal functionality that I was aiming to implement for MVP, and I already had the Azure Functions project, so I figured I’d just stand up a few HTTP-triggered functions for my API. After all, I could always refactor them into their own proper web API project later.

It was midway through implementing authentication that I realized that rather than continuing to try to fit the endpoints that I needed into Azure Functions, it made sense to move the HTTP-triggered functions into their own web API project with a separate app service in Azure sooner rather than later.

So that’s what I did.

I performed the refactor Thursday/Friday, and fiddled with the build and release pipelines in Azure DevOps in my free moments on Friday/Saturday. Monday morning I switched the app to use the new API.

Thankfully the refactoring of the code was fairly simple because my functions, much like good controller methods, were thin – they simply received the HTTP request, deserialized it, performed any necessary request-level validation, and delegated processing to the domain layer which lives in a separate assembly. The controllers in the Web API project that I created ended up being very similar.

I’m now closer to the MVP infrastructure that I mentioned a week ago, depicted below (I’m just missing StockAlerts.WebApp now):

Stock Alerts MVP Infrastructure Resources

I love the feeling of checking items off of my TODO list.

Why I Chose Web API Over HTTP-Triggered Azure Functions

So why did I choose to move my API methods from my Functions project to their own Web API project and service?

A few key reasons: 1. Inability to configure middleware for Azure Functions 2. Prefer controller methods over Azure Functions 3. API usage patterns

Let’s talk about these one-by-one…

ASP.NET Core Middleware

ASP.NET Core gives the developer the ability to plug in logic into the request/response pipeline. This logic is referred to as middleware. Andrew Lock has a great post on what it is and how it works in an ASP.NET Core web app.

ASP.NET Core has default middleware that it executes during the normal course of processing a request and response, but it also allows the developer to configure at startup what additional middleware should execute with each request, including custom middleware. Middleware is generally used for performing cross-cutting tasks – things like logging, handling exceptions, rendering views, and performing authentication, to name a few.

Early in my adventures into Azure Functions I learned that the developer doesn’t have the ability to configure the middleware that executes during an HTTP-triggered function invocation. Sure, some folks have rolled their own middleware pattern in Azure Functions (like here and here), but I didn’t want to invest that much effort into building something that an ASP.NET Core Web API gives me for free.

My custom middleware needs aren’t too many: for a typical web API I add custom error-handling middleware and enable authentication middleware.

Though I was able to implement workarounds to accomplish these tasks to work in my functions, they weren’t nearly as clean as accomplishing the same thing with middleware in an ASP.NET Core Web API.

Error Handling

My preferred approach to handling exceptions on recent Web API projects has been to create an ErrorHandlingMiddleware class that catches any unhandled exception during the processing of the request and turns it into the appropriate HTTP response. The code can be found here. Adding it to the pipeline is as simple as one line in Startup.cs:

app.UseMiddleware(typeof(ErrorHandlingMiddleware));

To accomplish similar functionality in my Azure Functions required an additional Nuget package (PostSharp), a custom attribute, and a [HandleExceptions] on top of all of my functions. Not terrible, but I’d rather not have the extra package and have to remember to manually decorate my functions to get error-handling.

Authentication/Authorization

To turn on token-based authentication/authorization for an ASP.NET Core Web API endpoint, you must configure the authentication, JWT bearer, and authorization options in Startup.cs, add the authentication middleware with app.UseAuthentication();, and decorate your controller methods with the [Authorize] attribute.

To implement token-based authentication/authorization on my Azure Functions, there wasn’t an easy way for me to simply decorate a function with an [Authorize] attribute and let the framework make sure that the user could invoke the function. Instead, for each function I had to use AuthorizationLevel.Anonymous and manually check for a valid ClaimsPrincipal and return new UnauthorizedResult() if there wasn’t one.

It worked, but it wasn’t pretty.

Beyond that, I had trouble getting it to add the Token-Expired header on responses when the auth token has expired. After switching over to Web API, this just works with the configuration I have in place.

Prefer Controllers Over Functions

As I began to add multiple HTTP-triggered functions that manipulated the same server resource, I grouped them into a single file per resource, similar to how controllers are often organized. But even though the methods were grouped like controllers, there were significant differences at the code level that cause me to prefer the controller implementations over the Azure Functions implementations.

Let’s compare the two side-by-side…

Here’s an Azure Function for getting an alert definition by ID:

[FunctionName("GetAlertDefinition")]
[HandleExceptions]
public async Task<IActionResult> GetAlertDefinitionAsync(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "alert-definitions/{alertDefinitionId}")] HttpRequest req,
    string alertDefinitionId)
{
    var claimsPrincipal = _authService.GetAuthenticatedPrincipal(req);
    if (claimsPrincipal == null)
        return new UnauthorizedResult();

    var alertDefinition = await _alertDefinitionsService.GetAlertDefinitionAsync(new Guid(alertDefinitionId));

    claimsPrincipal.GuardIsAuthorizedForAppUserId(alertDefinition.AppUserId);

    var resource = _mapper.Map<Resources.Model.AlertDefinition>(alertDefinition);
    return new OkObjectResult(resource);
}

And here’s the analogous controller method for getting an alert definition by ID:

[HttpGet]
[Route("{alertDefinitionId}")]
public async Task<IActionResult> GetAsync(Guid alertDefinitionId)
{
    var alertDefinition = await _alertDefinitionsService.GetAlertDefinitionAsync(alertDefinitionId);

    HttpContext.User.GuardIsAuthorizedForAppUserId(alertDefinition.AppUserId);

    var resource = _mapper.Map<Resources.Model.AlertDefinition>(alertDefinition);
    return new OkObjectResult(resource);
}

Observations:

  1. The controller method is shorter.
  2. The controller method is able to accept ID parameters as GUIDs, avoiding manual conversion.
  3. The controller method declares the HTTP verb and route more cleanly (in my opinion) as method attributes rather than a parameter attribute.
  4. Because I’ve decorated the controller with [Authorize], the controller method avoids the manual authorization logic.
  5. Because I’m using the ErrorHandlingMiddleware, the controller method avoids the extra [HandleExceptions] attribute.
  6. Not illustrated in this example, but the controller method accepts request bodies as entities, avoiding having to manually deserialize the request body from the HttpRequest.

From a purely code aesthetics perspective, I just prefer controller methods over HTTP-triggered functions.

API Usage Pattern

I expect the usage pattern of my API to be fairly uniform across the available endpoints and the traffic to ebb and flow predictably with the amount of users using the mobile app. I don’t expect large spikes in traffic to specific endpoints where I would need to be able to scale individual endpoints; if there are large spikes due to a sudden increase in the number of users, I’ll want to scale the whole web API service.

While HTTP-triggered Azure Functions may be the right choice for other use cases, the anticipated usage pattern of the Stock Alerts API aligns much more closely with a Web API service.

I’m still using Azure Functions for pulling stock quotes, evaluating alert definitions, and sending out notifications. Azure Functions are well-suited for these use cases, for the reasons I described here.

Wrapping Up

With this change behind me, I’m ready to continue moving forward working on the mobile app. My mornings the rest of this week will be focused on building the Create Alert Definition screen.

Here’s the repository for the project if you’d like to follow along: https://github.com/jonblankenship/stock-alerts.

Thanks for reading!

-Jon


If you’d like to receive new posts via e-mail, consider joining the newsletter.

Stock Alerts Update 2019.07.03

I’d been meaning to get this update out over the weekend, but a stomach bug visited our house and threw off my schedule. I’d like to get these updates out about once a week going forward, but since this is a side project and I’m working on it for fun in my off hours, I’m not going to sweat it too much.

Also, as I mentioned in my first post, these updates will be pretty informal and unpolished. I just want to talk in detail about some of the things I did in the past week on the project, and what I plan to do in the coming week.

Last Week

Writing

With my announcement last weekend that I’ll be building Stock Alerts in public, I was compelled to write a few extra posts to lay some of the groundwork for project. I wrote the introductory post, spoke about the features, and laid out the infrastructure.

Naturally, this took some of my time away from development, but I think it was time well spent.

I have other posts that I want to write in the future to cover some of the work I’ve already done (particularly in the API), and I’ll try to work those in in the coming weeks without sacrificing too much dev time.

Create Alert Definition – Stock Search

I’ve been working on the Create Alert Definition screen in the Stock Alerts mobile app. This is where the user defines an alert, including selecting the stock and defining the alert criteria. Specifically, I was focused on the stock selection functionality last week (we’ll talk more about building the alert criteria in a couple weeks).

Here’s a wireframe for these screens:

Stock Alerts Create Alert Definition screen wireframes

I want the stock search feature to function like a typeahead search, allowing the user to type as much or as little of the stock symbol or company name as desired, and when they pause, the system retrieves the search results.

I already had an API endpoint for finding stocks based on a search string; I just needed to add CancellationToken support, which was as simple as adding it to the Azure function signature and plumbing it down to the data access layer:

[FunctionName("FindStocks")]
[HandleExceptions]
public async Task<IActionResult> FindStocksAsync(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "stocks")] HttpRequest req,
    CancellationToken cancellationToken,
    ILogger log)

Implementing search on the mobile app side took a bit more work…

Thinking about this from an implementation perspective, my StockSearchPageViewModel needs to have a SearchString property that receives the input from the textbox, waits a second, and if there’s no additional input, execute the web request to get the search results from the API, which will populate a collection of results on the view model to which the view is bound. If additional input is received from the user while the web request is executing, we need to cancel it and issue a new request.

I can’t (shouldn’t) implement all of this in the SearchString property setter, because you can’t (and shouldn’t want to) make a property setter async. Property setters should be fast and non-blocking. And yet I want to be able to simply bind the Text property of my search box to a property on my view model.

I ended up using NotifyTask from Stephen Cleary’s Nito.Mvvm.Async library, which contains helpers for working with async methods in MVVM. NotifyTask is “essentially an INotifyPropertyChanged wrapper for Task/Task<T>,” as Stephen writes in this SO answer, which helped me quite a bit (the answer refers to NotifyTaskCompletion, which was replaced by NotifyTask).

So here’s my StockSearchPageViewModel implementation:

public class StockSearchPageViewModel : ViewModelBase
{
    private readonly IStocksService _stocksService;
    private CancellationTokenSource _searchCancellationTokenSource;

    public StockSearchPageViewModel(
        IStocksService stocksService,
        INavigationService navigationService, 
        ILogger logger) : base(navigationService, logger)
    {
        _stocksService = stocksService ?? throw new ArgumentNullException(nameof(stocksService));
    }

    private string _searchString;
    public string SearchString
    {
        get => _searchString;
        set
        {
            _searchString = value;

            var newSearchCancellationTokenSource = new CancellationTokenSource();
            if (_searchCancellationTokenSource != null)
                _searchCancellationTokenSource.Cancel();
            _searchCancellationTokenSource = newSearchCancellationTokenSource;

            Stocks = NotifyTask.Create(SearchStocksAsync(_searchCancellationTokenSource));
            RaisePropertyChanged(nameof(SearchString));
        }
    }

    private NotifyTask<List<Stock>> _stocks;
    public NotifyTask<List<Stock>> Stocks
    {
        get => _stocks;
        set
        {
            _stocks = value;
            RaisePropertyChanged(nameof(Stocks));
        }
    }

    private Stock _stock;
    public Stock SelectedStock
    {
        get => _stock;
        set
        {
            _stock = value;
            var navigationParams = new NavigationParameters();
            navigationParams.Add(NavigationParameterKeys.SelectedStock, _stock);
            NavigationService.GoBackAsync(navigationParams);
        }
    }

    private async Task<List<Stock>> SearchStocksAsync(CancellationTokenSource searchCancellationTokenSource)
    {
        if (SearchString.Length >= 1)
        {
            await Task.Delay(1000, searchCancellationTokenSource.Token);
            try
            {
                if (!searchCancellationTokenSource.IsCancellationRequested)
                {
                    var stocks = await _stocksService.FindStocksAsync(SearchString, searchCancellationTokenSource.Token);
                    return stocks.ToList();
                }
            }
            finally
            {
                searchCancellationTokenSource.Dispose();
                _searchCancellationTokenSource = null;
            }
        }

        return new List<Stock>();
    }
}

The view model creates and manages the cancellation token source, and cancels it when necessary, in the SearchString property setter. This is also where we create the NotifyTask, passing it a delegate for the SearchStocksAsync(..) method, which delays one second and calls the search API. The results of the SearchStocksAsync(..) method call are exposed as NotifyTask<List<Stock>> by the Stocks property.

In my StockSearchPage view, I can simply bind to the properties, like so:

<SearchBar Grid.Row="1" Placeholder="Start typing ticker or company name" Text="{Binding SearchString, Mode=TwoWay}"></SearchBar>
<ListView Grid.Row="2" ItemsSource="{Binding Stocks.Result}" SelectedItem="{Binding SelectedStock}">
    <!--snip-->
</ListView>

… and with that, the typeahead stock search seems to be working pretty well.

HttpClientFactory

ASP.Net Core 2.1 introduced HttpClientFactory, which solves some of the problems developers run into when they create too many HttpClients in their projects. Steven Gordon has a nice write-up on HttpClientFactory and the problems it attempts to solve here.

The syntax to configure clients using HttpClientFactory is straightforward. In your ASP.NET Core Startup.cs:

services.AddHttpClient(Apis.SomeApi, c =>
{
    c.BaseAddress = new Uri("https://api.someapi.com");
    c.DefaultRequestHeaders.Add("Accept", "application/json");
});

Unfortunately, since Xamarin.Forms projects target .NET Standard, we can’t use any of the .NET Core goodies like HttpClientFactory. I wanted a similar pattern for configuring and creating my HttpClients in the mobile app, so I took some inspiration from here and created my own poor man’s HttpClientFactory.

Here’s my IHttpClientFactory interface:

public interface IHttpClientFactory
{
    void AddHttpClient(string name, Action<HttpClient> configurationAction);

    HttpClient CreateClient(string name);
}

And here’s my fairly naïve, yet adequate implementation:

public class HttpClientFactory : IHttpClientFactory, IDisposable
{
    private readonly IDictionary<string, Action<HttpClient>> _configurations = new Dictionary<string, Action<HttpClient>>();
    private readonly IDictionary<string, HttpClient> _clients = new Dictionary<string, HttpClient>();

    public void AddHttpClient(string name, Action<HttpClient> configurationAction)
    {
        if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name), $"{nameof(name)} must be provided.");
        if (_configurations.ContainsKey(name)) throw new ArgumentNullException(nameof(name), $"A client with the name {name} has already been added.");

        _configurations.Add(name, configurationAction);
    }

    public HttpClient CreateClient(string name)
    {
        if (!_clients.ContainsKey(name))
        {
            if (!_configurations.ContainsKey(name)) throw new ArgumentException($"A client by the name of {name} has not yet been registered.  Call {nameof(AddHttpClient)} first.");

            var httpClient = new HttpClient();
            _configurations[name].Invoke(httpClient);
            _clients.Add(name, httpClient);
        }

        return _clients[name];
    }

    public void Dispose()
    {
        foreach (var c in _clients)
        {
            c.Value.Dispose();
        }
    }
}

Finally, the registration of the factory with a single client in my App.xaml.cs:

IHttpClientFactory httpClientFactory = new HttpClientFactory();
httpClientFactory.AddHttpClient(
    MiscConstants.StockAlertsApi, 
    c =>
    {
        c.BaseAddress = new Uri(MiscConstants.StockAlertsApiBaseUri);
        c.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    });
containerRegistry.RegisterInstance(httpClientFactory);

This gives me a nice way to create and manage my HttpClients in my Xamarin.Forms project, and it will be easy to drop in the real HttpClientFactory if it ever becomes available for Xamarin.Forms projects.


Last week’s activities also included implementing a web service client base class for handling common tasks when communicating with the API, storing access and refresh tokens on the client, and working out my unauthorized/refresh token flow, but those are topics for another post. This one’s long enough.

This Week

This week’s already about half over, and we’ve got the 4th of July coming up. I plan to continue working on the Create Alert Definition screen, and perhaps by the next time I write I’ll have the functionality for building the alert criteria and saving the alert definition working – we’ll see.

Here’s the repository for the project if you’d like to follow along: https://github.com/jonblankenship/stock-alerts.

Thanks for reading, and Happy Fourth of July!

-Jon


If you’d like to receive new posts via e-mail, consider joining the newsletter.

Stock Alerts Infrastructure

We’ve talked about the features that we’ll be implementing in Stock Alerts. Today we’ll look at the infrastructure that will be needed to support those features.

I’ve been working in Azure for several years now, both in my work life and on side projects. Being a primarily .NET developer, it makes sense that Azure is my preferred cloud. One of these days I will probably check out AWS, but for this project we’ll be hosting our backend services in Azure.

Microsoft Azure

More Than Just CRUD

When deciding what to build for this project, I wanted to do something that was a bit more than just a simple CRUD app that consists of an app talking to a web service talking to a database. Stock Alerts will need to continuously monitor the current prices of all stocks for which there are active alert definitions and evaluate whether the alert should be triggered, so we’ll need a process that runs on a regular basis to perform that work. Further, when the process detects that an alert should be triggered, it needs to send out notifications on the user’s preferred channel(s).

For this processing, we’ll use a combination of Azure Functions and Service Bus queues.

Here’s a sequence diagram depicting the retrieving of quotes, evaluation of alert definitions, and sending of notifications:

Stock Alerts Notification Sequence Diagram

Alert Definition Evaluation

The evaluation of the active alert definitions will have a predictable load. The system will query the stock data provider on a defined time interval for the distinct set of stocks for which there are active alerts and iterate through the alert definitions and evaluate them against the latest data received from the data provider for that stock.

A timer-triggered Azure Function, which is essentially a CRON job running in Azure, will work nicely for periodically pulling new stock data. Initially, there will be a single function instance to pull the data, but this work can be partitioned out to multiple function instances if/when the need arises. It will then enqueue a message on a service bus queue (alertprocessingqueue) for each active alert indicating that there’s new data and the alert needs to be evaluated.

A service bus queue-triggered function (EvaluateAlert) will receive the service bus message and perform the evaluation for a single alert definition.

Sending Notifications

The actual notification of users, on the other hand, will likely be characterized by periods of low activity with occasional spikes of high activity. Imagine a very popular stock like AAPL receiving an earnings surprise and opening 5% higher – several alert definitions could be triggered at once and notifications will need to be sent immediately.

Azure Functions will help us with this use case as well – we’ll enqueue notification messages on service bus queues (pushnotificationqueue, for example) when alerts are triggered and service bus queue-triggered functions (SendPushNotification, for example) will respond and send out the notifications. We’ll have a queue for each delivery channel (push, e-mail, SMS), and a function for each as well.

When AAPL spikes and 500 alerts are triggered, 500 messages will be enqueued on service bus queues (assuming each user only has one delivery channel) and 500 functions will be invoked to deliver those notifications.

The Infrastructure

So what Azure resources will be required to support the Stock Alerts features? Here’s a diagram of what we’ll need for the MVP:

Stock Alerts MVP Infrastructure Resources

We’ve got an Azure SQL database to store our users and their preferences, alert definitions and criteria, and stocks and their latest data.

We’ve already talked about the service bus queues, which are used for communicating between the Azure Functions, and we’ve already talked about the Azure Functions as well.

The Stock Alerts API will be an ASP.NET Core Web API service running in Azure, and it will expose endpoints to handle the user and alert definition maintenance as well as authentication.

The Stock Alerts web app, though depicted on the diagram, will actually be implemented post-MVP.

Current State

The above shows the infrastructure as I plan to have it at launch. Below is the current infrastructure I have deployed in Azure:

Stock Alerts Current Infrastructure Resources

All of the API endpoints are currently implemented as HTTP-triggered Azure functions. I did this because I already had the StockAlerts.Functions project, and I didn’t think there’d be that many HTTP endpoints. As I started implementing the authentication endpoints and I ran into some of the limitations of Azure Functions HTTP endpoints (i.e., you can’t inject logic into the pipeline as you can into the ASP.NET Core middleware for a full-fledged Web API), I increasingly felt like the API endpoints deserved their own project and app service. It’s on my TODO list to move these into their own project and service.

Wrapping Up

I think the most interesting part of the Stock Alerts infrastructure is the use of Azure Functions and Service Bus queues to evaluate alert definitions and send notifications. Azure Functions make sense for these processes because they can be triggered by schedule or service bus queue message (among other methods), and they are easily scaled. Service bus queues are appropriate for the communication between functions because they are highly available, reliable, and fast.

Though one of the key value props of serverless computing is automatic scaling, I don’t have practical experience with scaling Azure Functions during periods of high load. I’ll log and monitor timings from the various functions to ensure that notifications are being delivered in a timely fashion from when they are triggered, which is crucial for Stock Alerts’ primary function.

That’s all for now. Thanks for reading.

-Jon


If you’d like to receive new posts via e-mail, consider joining the newsletter.