Working with DocumentDb

In my last post, I introduced working with HTTP CRUD APIs with Azure Functions. My intent in all this is to create a proof of concept service that emulates the Azure Mobile Apps service, but using Azure Functions and the dynamic (or consumption-based) SKU. This means that you pay for the API only when it is being used, but it scales seamlessly as your needs grow. In addition, I’m going to make the backing store for this API a NoSQL store based on another Azure resource – DocumentDb.

Fortunately for me, DocumentDb has a nice Node.js driver. I’m going to promisify the callback-based SDK with bluebird. There are a number of samples available for the DocumentDb driver. For instance, here is my docdb-driver/database.js file:

module.exports = {
    createDatabase: function (client, databaseId, callback) {
        client.createDatabase({ id: databaseId }, callback);
    },

    deleteDatabase: function (client, databaseId, callback) {
        client.deleteDatabase(`dbs/${databaseId}`, callback);
    },

    findDatabaseById: function (client, databaseId, callback) {
        var qs = {
            query: 'SELECT * FROM root r WHERE r.id = @id',
            parameters: [
                { name: '@id', value: databaseId }
            ]
        };

        client.queryDatabases(qs).toArray(function (err, results) {
            if (err) {
                callback(err, null);
            } else {
                callback(null, (results.length === 0) ? null : results[0]);
            }
        });
    },

    listDatabases: function (client, callback) {
        client.readDatabases().toArray(callback);
    },

    readDatabase: function (client, database, callback) {
        client.readDatabase(database._self, callback);
    },

    readDatabases: function (client, databaseId, callback) {
        client.readDatabase(`dbs/${databaseId}`, callback);
    }
};

This is based on callbacks, rather than promises. So my docdb-driver/index.js file uses promisify to convert them to promises:

var Promise = require('bluebird');
var collection = require('./collection');
var database = require('./database');
var docops = require('./document');

var dbCache = {};

var createDatabase = Promise.promisify(database.createDatabase);
var findDatabaseById = Promise.promisify(database.findDatabaseById);

function ensureDatabaseExists(client, database) {
    if (database in dbCache) {
        return Promise.resolve(dbCache[database]);
    }

    return findDatabaseById(client, database).then((dbRef) => {
        if (dbRef == null) {
            return createDatabase(client, database).then((result) => {
                dbCache[database] = result;
                return result;
            });
        }
        dbCache[database] = dbRef;
        return dbRef;
    });
}

module.exports = {
    createCollection: Promise.promisify(collection.createCollection),
    listCollections: Promise.promisify(collection.listCollections),
    readCollection: Promise.promisify(collection.readCollection),
    readCollectionById: Promise.promisify(collection.readCollectionById),
    getOfferType: Promise.promisify(collection.getOfferType),
    changeOfferType: Promise.promisify(collection.changeOfferType),
    deleteCollection: Promise.promisify(collection.deleteCollection),

    createDatabase: createDatabase,
    deleteDatabase: Promise.promisify(database.deleteDatabase),
    ensureDatabaseExists: ensureDatabaseExists,
    findDatabaseById: findDatabaseById,
    listDatabases: Promise.promisify(database.listDatabases),
    readDatabase: Promise.promisify(database.readDatabase),
    readDatabases: Promise.promisify(database.readDatabases),

    createDocument: Promise.promisify(docops.createDocument)
};

I’m going to extend this driver package over time. Sometimes I use the straight API from the DocumentDb driver (see the readDatabase() method). Sometimes, however, I want to do something extra. The ensureDatabaseExists() method is an example of this. I want to find the database in the service and create it only if it doesn’t exist.

Back to the Azure Function I’m developing. DocumentDb mainly stores “documents” – JSON blobs of associated data. It organizes these documents into “collections” and collections into a “database”. In the Azure Mobile Apps equivalent, the collection would be a table and the individual rows or entities would be documents. My first requirement is to ensure that the database and collection are initialized properly (in todoitem/index.js):

var DocumentDb = require('documentdb');
var driver = require('../docdb-driver');

/**
 * Global Settings Object
 */
var settings = {
    host: process.env['DocumentDbHost'],
    accountKey: process.env['DocumentDbAccountKey'],
    database: 'AzureMobile',
    connectionPolicy: undefined,
    consistencyLevel: 'Session',
    pricingTier: 'S1',
    table: 'todoitem'
};

// Store any references we receive here as a cache
var refs = {
    initialized: false
};

/**
 * Routes the request to the table controller to the correct method.
 *
 * @param {Function.Context} context - the table controller context
 * @param {Express.Request} req - the actual request
 */
function tableRouter(context, req) {
    var res = context.res;
    var id = context.bindings.id;

    initialize(context).then(() => {
        switch (req.method) {
            case 'GET':
                if (id) {
                    getOneItem(req, res, id);
                } else {
                    getAllItems(req, res);
                }
                break;

            case 'POST':
                insertItem(req, res);
                break;

            case 'PUT':
                replaceItem(req, res, id);
                break;

            case 'DELETE':
                deleteItem(req, res, id);
                break;

            default:
                res.status(405).json({ error: "Operation not supported", message: `Method ${req.method} not supported`})
        }
    });
}

/**
 * Initialize the DocumentDb Driver
 * @param {Function.Context} context - the table controller context
 * @param {function} context.log - used for logging
 * @returns {Promise}
 */
function initialize(context) {
    if (refs.initialized) {
        context.log('[initialize] Already initialized');
    }

    context.log(`[initialize] Creating DocumentDb client ${settings.host} # ${settings.accountKey}`);
    refs.client = new DocumentDb.DocumentClient(
        settings.host,
        { masterKey: settings.accountKey },
        settings.connectionPolicy,
        settings.consistencyLevel
    );

    context.log(`[initialize] EnsureDatabaseExists ${settings.database}`);
    return driver.ensureDatabaseExists(refs.client, settings.database)
        .then((dbRef) => {
            context.log(`[initialize] Initialized Database ${settings.database}`);
            refs.database = dbRef;
            return driver.listCollections(refs.client, refs.database);
        })
        .then((collections) => {
            context.log(`[initialize] Found ${collections.length} collections`);
            const collection = collections.find(c => { return (c.id === settings.table); });
            context.log(`[initialize] Collection = ${JSON.stringify(collection)}`);
            if (typeof collection !== 'undefined') return collection;
            context.log(`[initialize] Creating collection ${settings.table}`);
            return driver.createCollection(refs.client, settings.pricingTier, refs.database, settings.table);
        })
        .then((collectionRef) => {
            context.log(`[initialize] Found collection`);
            refs.table = collectionRef;
            refs.initialized = true;
        });

    context.log('[initialize] Finished Initializing Driver');
}

Let's take this in steps. Firstly, I set up the settings. The important things here are the DocumentDbHost and the DocumentDbAccountKey. If you have created a DocumentDb within the Azure Portal, click on the Keys menu item. The DocumentDbHost is the URI field and the DocumentDbAccountKey is the PRIMARY KEY field. If you are running the Azure Function locally, then you will need to set these as environment variables before starting the func host. If you are running the Azure Function within Azure, you need to make these App Settings. An example of setting these locally in PowerShell:

$env:DocumentDbHost = "https://mydocdb.documents.azure.com:443/"
$env:DocumentDbAccountKey = "fuCZuSomeLongStringaLNKjIiMSEyaojsP05ywmevI7K2yCY9dYLRuCQPd3dMnvg=="
func run test-func --debug

When you use Postman (for example, a GET http://localhost:7071/tables/todoitem), you will see the initialize() method gets called. This method returns a Promise that, when resolved, will then allow the request to be continued. In the initialize() method, I short-circuit the initialization if it has already been initialized. If it has not been initialized, I fill in the refs object. This object will be used by the inidividual CRUD operations, so it needs to be filled in. The client, database, and collection that we need are found or created. At the end, we have resolve the promise by setting the initialized flag to true (thus future calls will be short circuited).

There is a race condition here. If two requests come in to a “cold” function, they will both go through the initialization together and potentially the “create database” and “create collection” will be duplicated, causing an exception in one of the requests. I’m sure I could fix this, but it’s a relatively rare case. Once the datbase and collection are created, the possibility of the condition goes away.

If you run this (either locally or within Azure Functions), you will see the following output in the log window:

function-docdb

If you’ve done something wrong, you will see the exception and you can debug it using the normal methods. Want a primer? I’ve written a blog post about it.

In the next post, I’ll cover inserting, deleting and updating records. Until then, check out the code on my GitHub repository.

Writing HTTP CRUD in Azure Functions

Over the last two posts, I’ve introduced writing Azure Functions locally and deploying them to the cloud. It’s time to do something useful with them. In this post, I’m going to introduce how to write a basic HTTP router. If you follow my blog and other work, you’ll see where this is going pretty quickly. If you are only interested in Azure Functions, you’ll have to wait a bit to see how this evolves.

Create a new Azure Function

I started this blog by installing the latest azure-functions-cli package:

npm i -g azure-functions-cli

Then I created a new Azure Function App:

mkdir dynamic-tables
cd dynamic-tables
func new

Finally, I created a function called todoitem:

dec15-01

Customize the HTTP Route Prefix

By default, any HTTP trigger is bound to /api/_function_ where function is the name of your function. I want full control over where my function exists. I’m going to fix this is the host.json file:

{
    "id":"6ada7ae64e8a496c88617b7ab6682810",
    "http": {
        "routePrefix": ""
    }
}

The routePrefix is the important thing here. The value is normally “/api”, but I’ve cleared it. That means I can put my routes anywhere.

Set up the Function Bindings

In the todoitem directory are two files. The first, function.json, describes the bindings. Here is the version for my function:

{
    "disabled": false,
    "bindings": [
        {
            "name": "req",
            "type": "httpTrigger",
            "direction": "in",
            "authLevel": "function",
            "methods": [ "GET", "POST", "PATCH", "PUT", "DELETE" ],
            "route": "tables/todoitem/{id:alpha?}"
        },
        {
            "type": "http",
            "direction": "out",
            "name": "res"
        }
    ]
}

This is going to get triggered by a HTTP trigger, and will accept five methods: GET, POST, PUT, PATCH and DELETE. In addition, I’ve defined a route that contains an optional string for an id. I can, for example, do GET /tables/todoitem/foo and this will be accepted. On the outbound side, I want to respond to requests, so I’ve got a response object. The HTTP Trigger for Node is modelled after ExpressJS, so the req and res objects are mostly equivalent to the ExpressJS Request and Response objects.

Write the Code

The code for this function is in todoitem/index.js:

/**
 * Routes the request to the table controller to the correct method.
 *
 * @param {Function.Context} context - the table controller context
 * @param {Express.Request} req - the actual request
 */
function tableRouter(context, req) {
    var res = context.res;
    var id = context.bindings.id;

    switch (req.method) {
        case 'GET':
            if (id) {
                getOneItem(req, res, id);
            } else {
                getAllItems(req, res);
            }
            break;

        case 'POST':
            insertItem(req, res);
            break;

        case 'PATCH':
            patchItem(req, res, id);
            break;

        case 'PUT':
            replaceItem(req, res, id);
            break;

        case 'DELETE':
            deleteItem(req, res, id);
            break;

        default:
            res.status(405).json({ error: "Operation not supported", message: `Method ${req.method} not supported`})
    }
}

function getOneItem(req, res, id) {
    res.status(200).json({ id: id, message: "getOne" });
}

function getAllItems(req, res) {
    res.status(200).json({ query: req.query, message: "getAll" });
}

function insertItem(req, res) {
    res.status(200).json({ body: req.body, message: "insert"});
}

function patchItem(req, res, id) {
    res.status(405).json({ error: "Not Supported", message: "PATCH operations are not supported" });
}

function replaceItem(req, res, id) {
    res.status(200).json({ body: req.body, id: id, message: "replace" });
}

function deleteItem(req, res, id) {
    res.status(200).json({ id: id, message: "delete" });
}

module.exports = tableRouter;

I use a tableRouter method (and that is what our function calls) to route the HTTP call to the write CRUD method I want to execute. It’s up to me to put whatever CRUD code I need to execute and respond to the request in those additional methods. In this case, I’m just returning a 200 status (OK) and some JSON data. One key piece is differentiating between a GET /tables/todoitem and a GET /tables/todoitem/foo. The former is meant to return all records and the latter is meant to return a single record. If the id is set, we call the single record GET method and if not, then we call the multiple record GET method.

What’s the difference between PATCH and PUT? In REST semantics, PATCH Is used when you want to do a partial update of a record. PUT is used when you want to send a full record. This CRUD recipe uses both, but you may decide to use one or the other.

Running Locally

As with the prior blog post, you can run func run test-func --debug to start the backend and get ready for the debugger. You can then use Postman to send requests to your backend. (Note: Don’t use func run todoitem --debug – this will cause a crash at the moment!). You’ll get something akin to the following:

dec15-02

That’s it for today. I’ll be progressing on this project for a while, so expect more information as I go along!

Deploying Azure Functions Automatically

In my last post, I went over how to edit, run and debug Azure Functions on your local machine. Eventually, however, you want to place these functions in the cloud. They are, after all, designed to do things in the cloud on dynamic compute. There are two levels of automation you can use:

  1. Continuous Deployment
  2. Automated Resource Creation

Most of you will be interested in continuous deployment. That is, you create your Azure Functions app once and then you just push updates to it via a source code control system. However, a true DevOps mindset requires “configuration as code”, so we’ll go over how to download an Azure Resource Manager (ARM) template for the function app and resource group.

Creating a Function App in the Portal

Creating an Azure Functions app in the portal is a straight forward process.

  1. Create a resource group.
  2. Create a function app in the resource group.

Log into the Azure portal. Select Resource Groups in the left-hand menu (which may be under the “More Services” link in your case), then click on the + Add link in the top bar to create a new resource group. Give it a unique name (it only has to be unique within your subscription), select a nominal location for the resource group, then click on Create:

create-rg-1

Once the resource group is created, click into it and then select + Add inside the resource group. Enter “Function App” in the search box, then select the same and click on Create

create-func-1

Fill in the name and select the region that you want to place the Azure Functions in. Ensure the “Consumption Plan” is selected. This is the dynamic compute plan, so you only pay for resources when your functions are actually being executed. The service will create an associated storage account for storing your functions in the same resource group.

Continuous Deployment

In my last blog post, I created a git repository to hold my Azure Function code. I can now link this git repository to the Function App in the cloud as follows:

  • Open the Function App.
  • Click the Function app settings link in the bottom right corner.
  • Click the Go to App Service Settings button.
  • Click the Deployment Credentials menu option.
  • Fill in the form for the deployment username and password (twice).
  • Click Save at the top of the blade.

You need to know the username and password of your git repository in the cloud that is attached to your Function App so that you can push to it. You’ve just set those credentials.

  • Click the Deployment options menu option.
  • Click Choose Source.
  • Click Local Git Repository
  • Click OK.

I could have just as easily linked my function app to GitHub, Visual Studio Team Services or BitBucket. My git repository is local to my machine, so a local git repository is suitable for this purpose.

  • Click the Properties menu option.
  • Copy the GIT URL field.

I now need to add the Azure hosted git repository as a remote on my local git repository. To do this, open a PowerShell console, change directory to the function app and type the following:

git remote add azure <the-git-url>
git push azure master

This will push the contents of the git repository up to the cloud, which will then do a deployment of the functions for you. You will be prompted for your username and password that you set when setting up the deployment credentials earlier.

create-func-2

Once the deployment is done, you can switch back to the Function App in the portal and you will see that your function is deployed. An important factor is that you are now editing the files associated with the function on your local machine. You can no longer edit the files in the cloud, as any changes would be overwritten by the next deployment from your local machine. To remind you of this, Azure Functions displays a helpful warning:

create-func-3

If you edit your files on the local machine, remember to push them to the Azure remote to deploy them.

Saving the ARM Template

You are likely to only need the Azure Function process shown above. However, in case you like checking in the configuration as code, here is how you do it. Firstly, go to your resource group:

create-rg-2

Note the menu item entitled Automation script – that’s the one you want. The portal will generate an Azure Resource Manager (ARM) template plus a PowerShell script or CLI script to run it. Click on Download to download all the files – you will get a ZIP file.

Before extracting the ZIP file, you need to unblock it. In the File Explorer, right-click on the ZIP file and select Properties.

create-rg-3

Check the Unblock box and then click on OK. You can now extract the ZIP file with your favorite tool. I just right-click and select Extract All….

Creating a new Azure Function with the ARM Template

You can now create a new Azure Function App with the same template as the original by running .\deploy.ps1 and filling in the fields. Yep – it’s that simple!

Creating and Debugging Azure Functions Locally

I’ve written about Azure Functions before as part of my Azure Mobile Apps series. Azure Functions is a great feature of the Azure platform that allows you to run custom code in the cloud in a “serverless” manner. In this context, “serverless” doesn’t mean “without a server”. Rather, it means that the server is abstracted away from you. In my prior blog post, I walked through creating an Azure Function using the web UI, which is a problem when you want to check your Azure Functions in to source code and deploy them as part of your application.

UPDATE: Azure App Service have released a blog on the new CLI tools.

This is the first in a series of blog posts. I am going to walk through a process by which you can write and debug Azure Functions on your Windows 10 PC, then check the code into your favorite SCCM and deploy in a controlled manner. In short – real world.

Getting Ready

Before you start, let’s get the big elephant out of the way. The actual runtime Windows only. Sorry, Mac Users. The run-time relies on the 4.x .NET Framework, and you don’t have that. Boot into Windows 10. You can still create functions locally, but you will have to publish them to the cloud to run them. There is no local runtime on a Mac.

To get your workstation prepped, you will need the following:

  • Node
  • Visual Studio Code
  • Azure Functions Runtime

Node is relatively easy. Download the Node package from nodejs.org and install it as you would any other package. You should be able to run the node and npm programs from your command line before you continue. Visual Studio Code is similarly easily downloaded and installed. You can download additional extensions if you like. If you write functions in C#, I would definitely download the C# extension.

The final bit is the Azure Functions Runtime. This is a set of tools produced by the Azure Functions team to create and run Functions locally and are based on Yeoman. To install:

npm install -g yo generator-azurefunctions azure-functions-cli

WARNING There is a third-party module called azure-functions which is not the same thing at all. Make sure you install the right thing!

After installing, the func command should be available:

func-1

Once you have these three pieces, you are ready to start working on Azure Functions locally.

Creating an Azure Functions Project

Creating an Azure Functions project uses the func command:

mkdir my-func-application
cd my-func-application
func init

Note that func init creates a git repository as well – one less thing to do! Our next step is to create a new function. The Azure Functions CLI uses Yeoman underneath, which we can call directly using yo azurefunctions:

func-2

You can create as many functions as you want in a single function app. In the example above, I created a simple HTTP triggered function written in JavaScript. This can be used as a custom API in a mobile app, for example. The code for my trigger is stored in test-func\index.js:

module.exports = function(context, req) {
    context.log('Node.js HTTP trigger function processed a request. RequestUri=%s', req.originalUrl);

    if (req.query.name || (req.body && req.body.name)) {
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: "Hello " + (req.query.name || req.body.name)
        };
    }
    else {
        context.res = {
            status: 400,
            body: "Please pass a name on the query string or in the request body"
        };
    }
    context.done();
};

and the binding information is in test-func\function.json:

{
    "disabled": false,
    "bindings": [
        {
            "authLevel": "function",
            "type": "httpTrigger",
            "direction": "in",
            "name": "req"
        },
        {
            "type": "http",
            "direction": "out",
            "name": "res"
        }
    ]
}

Running the function

To run the Azure Functions Runtime for your function app, use func run test-func.

The runtime is kicked off first. This monitors the function app for changes, so any changes you do in the code will be reflected as soon as you save the file. If you are running something that can be triggered manually (like a cron job), then it will be run immediately. For my HTTP trigger, I need to hit the HTTP endpoint – in this case, http://localhost:7071/api/test-func.

Note that the runtime is running with the version of Node that you installed and it is running on your local machine. Yet it still can be triggered by whatever you set up. If you set up a blob trigger from a storage account, then that will trigger. You have to set up the environment properly. Remember that App Service (and Functions) app settings appear as environment variables to the runtime. When you run locally, you will need to manually set up the app settings by setting an environment variable of the same name. Do this before you use func run for the first time.

Debugging the function

Running the function is great, but I want to debug the function – set a breakpoint, inspect the internal state of the function, etc. This can be done easily in Visual Studio Code as the IDE has an integrated Node debugger.

  • Run func run test-func --debug
  • Go into the debugger within Visual Studio Code, and set a breakpoint
  • Switch to the Debugger tab and hit Start (or F5)

    func-3

  • Trigger the function. Since I have a HTTP triggered function, I’m using Postman for this:

    func-4

  • Note that the function is called and I can inspect the internals and step through the code:

    func-5

You can now step through the code, inspect local variables and generally debug your script.

Next up

In the next article, I’ll discuss programmatically creating an Azure Function app and deploying our function through some of the deployment mechanisms we have available to us in Azure App Service.

Offline Sync with Azure Mobile Apps and Apache Cordova

In the past, I’ve introduced you to a TodoList application built in Apache Cordova so that it is available for iOS, Android or any other platform that Apache Cordova supports. Recently, we released a new beta for the Azure Mobile Apps Cordova SDK that supports offline sync, which is a feature we didn’t have.

Underneath, the Cordova offline sync functionality uses SQLite – this means it isn’t an option at this point for HTML/JS applications. We’ll have to work out how to do this with IndexDB or something similar, but for now that isn’t an option without a lot of custom work.

Let’s take a look at the differences.

Step 1: New variables

Just like other clients, I need a local store reference and a sync context that is used to keep track of the operational aspects for synchronization:

    var client,        // Connection to the Azure Mobile App backend
        store,         // Sqlite store to use for offline data sync
        syncContext,   // Offline data sync context
        todoItemTable; // Reference to a table endpoint on backend

Step 2: Initialization

All the initialization is done in the onDeviceReady() method. I have to set up a model so that the SQLite database is set up to match what is on the server:

function onDeviceReady() {

    // Create the connection to the backend
    client = new WindowsAzure.MobileServiceClient('https://yoursite.azurewebsites.net');

    // Set up the SQLite database
    store = new WindowsAzure.MobileServiceSqliteStore();

    // Define the table schema
    store.defineTable({
        name: 'todoitem',
        columnDefinitions: {
            // sync interface
            id: 'string',
            deleted: 'boolean',
            version: 'string',
            // Now for the model
            text: 'string',
            complete: 'boolean
        }
    }).then(function () {
        // Initialize the sync context
        syncContext = client.getSyncContext();
        syncContext.pushHandler = {
            onConflict: function (serverRecord, clientRecord, pushError) {
                window.alert('TODO: onConflict');
            },
            onError: function(pushError) {
                window.alert('TODO: onError');
            }
        };
        return syncContext.initialize(store);
    }).then(function () {
        // I can now get a reference to the table
        todoItemTable = client.getSyncTable(tableName);

        refreshData();

        $('#add-item').submit(addItemHandler);
        $('#refresh').on('click', refreshData);
    });
}

There are three distinct areas here, separated by promises. The first promise defines the tables. If you are using multiple tables, you must ensure that all promises are complete before progressing to the next section. You can do this with Promise.all() as an example.

The second section initializes the sync context. You need to define two sections for the push handler – the conflict handler and the error handler. I’ll go into the details of a conflict handler at a later date, but this is definitely something you will want to spend some time thinking about. Do you want the last one in to be the winner, or the current client edition to be the winner, or do you want to prompt the user on conflicts? It’s all possible.

Once I have created a sync context, I can get a reference to the local SQLite database table, which is used instead of the getTable() call that it replaces. The rest of the code is identical – I refresh the data and add the event handlers.

Step 3: Adjusting the Refresh

In the past, refresh was just a query to the backend. Now I need to do something a bit different. When refresh is clicked, I want to do the push/pull cycle for synchronizing the data.

function refreshData() {
    updateSummaryMessage('Loading data from Azure');
    syncContext.push().then(function () {
        return syncContext.pull(new WindowsAzure.Query('todoitem'));
    }).then(function () {
        todoItemtable
            .where({ complete: false })
            .read()
            .then(createTodoItemList, handleError);
    });
}

Just like the initialization, the SDK uses promises to proceed asynchronously. First push (which resolves as a promise), then pull (which also resolves as a promise) and finally you do EXACTLY THE SAME THING AS BEFORE – you query the table, read the results and then build the todo list. Seriously – this bit really didn’t change.

That means you can add offline to your app without changing your existing code – just add the initialization and something to trigger the push/pull.

Wrap Up

This is still a beta, which means a work-in-progress. Feel free to try it out and give us feedback. You can file issues and ideas at our GitHub repository.

Cross-posted to the Azure App Service Team Blog.

Using ModernHttpClient with Azure Mobile Apps

Azure Mobile Apps comes with a great client SDK that creates a client connection on its own. In the .NET library, that’s provided by System.Net.Http.HttpClient. If you take a look at the Xamarin implementation of System.Net.Http.HttpClient (for example, for Android), you should note that it uses raw I/O to a TCP/IP socket – the underlying building block of all internet communication that dates way back to the origins of the Internet and TCP/IP.

So what, you might ask? Well, doing raw I/O may not be the best way of communicating with a backend on all platforms. Both iOS and Android contains in-built raw libraries for doing HTTP and they deal with the underlying OS libraries to be more efficient. In the Android case, that’s HttpUrlConnection and in the iOS case, that’s NSURLConnection. In addition, there are other libraries that offer improvements for simple operations. Your client may be doing other (non-Mobile Apps) operations for this.

Fortunately, there is an answer. Paul Betts has produced a nice client alternative as a Portable Class Library called modernhttpclient and it’s open-source. The Portable Class Library (or PCL) is important because it means it can run on Xamarin as well as Windows .NET projects.

Now, I’m not saying you HAVE to use ModernHttpClient. Far from it – Azure Mobile Apps is perfectly fine as-is and you won’t have any problems. If you want to use ModernHttpClient, then go for it – it “just works” – almost.

ModernHttpClient is a drop-in replacement for System.Net.Http.HttpClient. You add the NuGet package and away you go. It “just works”. If you really want to go all-in, then the suggestion is to replace the HttpClient constructor with this:

var httpClient = new HttpClient(new NativeMessageHandler());

Here is where the “almost” comes in. Azure Mobile Apps constructs the httpClient with a set of handlers that it creates. You can add things to the list of handlers in the MobileServiceClient constructor, but you can’t affect the actual constructor of the HttpClient that the MobileServiceClient uses.

Fortunately, this is easy to integrate. Normally, you would create a MobileServiceClient like this:

var client = new MobileServiceClient("https://mysite.azurewebsites.net");

Instead, you add the message handler on the end:

var client = new MobileServiceClient("https://mysite.azurewebsites.net",
    new ModernHttpClient.NativeMessageHandler());

This will then adjust the HttpClient accordingly.

Multiple Message Handlers?

If you have read all the documentation and my blog series, you will note that this is acting as just another message handler. You can actually specify multiple message handlers, but – and this is important – the NativeMessageHandler() MUST be the last one listed. For instance, let’s say you’ve defined a Delegating Handler to do some logging. You would need to do the following:

var handlers = new HttpMessageHandler[]
{
    new LoggingDelegatedHandler(),
    new NativeMessageHandler()
};
var client = new MobileServiceClient("https://mysite.azurewebsites.net", handlers);

Consider the request as going through layers of an onion, with the request actually being done by the inner-most layer. The order of the layers is listed in the array with the outer-most layer listed first.

Adjusting the HTTP Request with Azure Mobile Apps

Azure Mobile Apps provides an awesome client SDK for dealing with common mobile client problems – data access, offline sync, Notification Hubs registration and authentication.  Sometimes, you want to be able to do something extra in the client.  Perhaps you need to adjust the headers that are sent, or perhaps you want to understand the requests by doing diagnostic logging.  Whatever the reason, Azure Mobile Apps is extensible and can easily handle these requirements.

Android (Native)

You can implement a ServiceFilter to manipulate requests and responses in the HTTP pipeline.  The general recipe is as follows:

ServiceFilter filter = new ServiceFilter() {
    @Override
    public ListenableFuture handleRequest(ServiceFilterRequest request, NextServiceFilterCallback next) {

        // Do pre-HTTP request requirements here
        request.addHeader("X-Custom-Header", "Header Value");  // Example: Adding a Custom Header
        Log.d("Request to ", request.getUrl());                // Example: Logging the request

        ListenableFuture responseFuture = next.onNext(request);

        Futures.addCallback(responseFuture, new FutureCallback() {
            @Override
            public void onFailure(Throwable exception) {
                // Do post-HTTP response requirements for failures here
                Log.d("Exception: ", exception.getMessage());  // Example: Logging an error
            }

            @Override
            public void onSuccess(ServiceFilterResponse response) {
                // Do post-HTTP response requirements for success here
                if (response != null &amp;&amp; response.getContent() != null) {
                    Log.d("Response: ", response.getContent());
                }
            }
        });
        
        return responseFuture;
    }
};

MobileServiceClient client = new MobileServiceClient("https://xxx.azurewebsites.net", this).withFilter(filter);

You can think of the ServiceFilter as a piece of middleware that wraps the existing request/response from the server.

iOS (Native)

Similar to the Android case, you can wrap the request in a filter. For iOS, the same code (once translated) works in both Swift and Objective-C. Here is the Swift version:

class CustomFilter: NSObject, MSFilter {

    func handleRequest(request: NSURLRequest, next: MSFilterNextBlock, response: MSFilterResponseBlock) {
        var mutableRequest: NSMutableURLRequest = request.mutableCopy()

        // Do pre-request requirements here
        if !mutableRequest.allHTTPHeaderFields["X-Custom-Header"] {
            mutableRequest.setValue("X-Custom-Header", forHTTPHeaderField: "Header Value")
        }

        // Invoke next filter
        next(customRequest, response)
    }
}

// In your client initialization code...
let client = MSClient(applicationURLString: "https://xxx.azurewebsites.net").clientWithFilter(CustomFilter())

The .clientWithFilter() method clones the provided client with the filters.

JavaScript & Apache Cordova

As you might exepct given the Android and iOS implementations, the JavaScript client (and hence the Apache Cordova implementation) also uses a filter – this is just a function that the request gets passed through:

function filter(request, next, callback) {
    // Do any pre-request requirements here
    console.log('request = ', request);                     // Example: Logging
    request.headers['X-Custom-Header'] = "Header Value";    // Example: Adding a custom here
    
    next(request, callback);
}

// Your client initialization looks like this...
var client = new WindowsAzure.MobileServiceClient("https://xxx.azurewebsites.net").withFilter(filter);

Xamarin / .NET

The equivalent functionality in the .NET world is a Delegating Handler. The implementation and functionality are basically the same as the others:

public class MyHandler: DelegatingHandler
{
    protected override async Task SendAsync(HttpRequestMessage message, CancellationToken token)
    {
        // Do any pre-request requirements here
        request.Headers.Add("X-Custom-Header", "Header Value");

        // Request happens here
        var response = await base.SendAsync(request, cancellationToken);

        // Do any post-request requirements here

        return response;
    }
}

// In your mobile client code:
var client = new MobileServiceClient("https://xxx.azurewebsites.net", new MyHandler());

General Notes

There are some HTTP requests that never go through the filters you have defined. A good example for this is the login process. However, all requests to custom APIs and/or tables get passed through the filters.

You can also wrap the client multiple times. For example, you can use two separate filters – one for logging and one for the request. In this case, the filters are executed in an onion-like fashion – The last one added is the outer-most. The request goes through each filter in turn until it gets to the actual client, then the response is passed through each filter on its way out to the requestor.

Finally, note that this is truly a powerful method allowing you to change the REST calls that Azure Mobile Apps makes – including destroying the protocol that the server and client rely on. Certain headers are required for Azure Mobile Apps to work, including tokens for authentication and API versioning. Use wisely.

(Cross-posted to the Azure App Service Blog)