Thursday, 15 March 2018

Azure Functions: Exception while executing function...connection string is missing or empty

I've recently started to play with Azure Functions trying them in Visual Studio with C# and in Visual Studio Code with JavaScript.

I've focused on the Http triggers specifically basic CRUD  interactions with Azure Table storage which will become a future series of blog posts.

My first attempt in C# went fairly smoothly, created a function then hit run and have Visual Studio handle starting the functions runtime which I then executed via Postman and it returned me data read from my local Azure Storage Emulator.

I then tried to recreate what I'd just done using JavaScript using the V1 functions creating the function via the azure-cli template with a minor alteration to read from my existing table.

The Problem

I started the function using func run TestGet and then tried to hit it using postman only to get an error:

Exception while executing function: Functions.TestGet -> Microsoft Azure WebJobs SDK 'MyStorageConnectionAppSetting' connection string is missing or empty. The Microsoft Azure Storage account connection string can be set in the following ways:
1. Set the connection string named 'AzureWebJobsMyStorageConnectionAppSetting' in the connectionStrings section of the .config file in the following format , or 
2. Set the environment variable named 'AzureWebJobsMyStorageConnectionAppSetting', or
3. Set corresponding property of JobHostConfiguration.

This confused me greatly as I had used the default AzureWebJobsStorage setting exactly as I had with the C# version which had worked.

Trying to Google the error message turned up next to no results and the majority of those didn't actually have any relevant infromation but this GitHub issue provided the clue to the answer specifically this comment which mention the IsEncrypted setting.

What I had noticed is that when the functions runtime started up the first line in the command window was The input is not a valid Base-64 string as it contains a non-base 64 character and so I checked my IsEncrypted setting

{
  "IsEncrypted": true,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true;"
  }
}

and found that it was set to true and due to that when the functions runtime ran up it wouldn't use the appsettings.json/local.settings.json as it expected them to be encrypted and since it wasn't it seems it wouldn't read the settings that were there, so setting it to false allowed the runtime to read the setting

The cause

A comment on the Github issue suggested that the IsEncrypted setting should be false so to  check if I had caused the issue or it had been the azure-cli I created a new folder and ran the command
func init which creates the basic files needed for functions including the appsettings.json which looks like this:

{
  "IsEncrypted": true,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true;"
  }
}

So it would seem the azure-cli is the cause of this issue and whilst you need to ensure the values should be encrypted when deployed to production/live when working locally you need them unencrypted to be able to work.

Why didn't it happen in Visual Studio?

I wanted to know why I hadn't run into this when I was creating functions in Visual Studio so went back into VS and created a new functions project and checked what had been created in the local.settings.json:

{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "UseDevelopmentStorage=true",
        "AzureWebJobsDashboard": "UseDevelopmentStorage=true"
    }
}

As you can see it sets the IsEncrypted to false which is why I never had the problem with the C# functions.  As an experiment I set IsEncrypted to true and tried to run the functions and interestingly the functions runtime reported an error Failed to decrypt settings. Encrypted settings only be edited through 'func settings add'. which whilst not exactly clear does give a better phrase to search for.

Hopefully if anybody else runs into this problem they'll find it easier to resolve than I did.

Monday, 12 February 2018

WebApi controller - using anonymous types as return values

On a couple of projects I’ve worked on recently both have used anonymous objects to return data to a SPA UI as Json from WebApi controllers.

Microsofts recommended return type for WebApi controller methods is IHttpActionResult and they provide a variety of the helper methods to make the creation of the response easy e.g. Ok(), BadRequst(), etc

To return an anonymous object as Json is as easy as using the Json method and create the anonymous object as the parameter to the method:

public IHttpActionResult Get()
{
    return Json(new { id = 1 });
}

Why do this?

There is a real advantage in using this technique as it gives you a lot of flexibility in what you return, there is no need to define different view model classes for each "shape" of data you need so it cuts down on the amount of boiler plate code needed.

Unit Testing

Not everybody writes unit tests for controller methods which return data but if you do the use of IHttpActionResult as a return type makes it a little trickier than you would anticipate to be able to look at the Json returned.

If you try to use the strongly typed classes (IHttpActionResult, HttpContent, etc) you'll most likely find yourself going down a rabbit hole trying to get to the content of the response which eventually either leads to having to use reflection to get the data or using dynamic.

However, if we take a short cut and make use of dynamic straight away we can vastly simplify the code which gives us a test that looks like this:

[Test]
public void Should_return_id_in_reponse()
{
    var target = new DataController();
 
    dynamic response = target.Get();
 
    dynamic content = response.Content;
 
    Assert.That(content.id, Is.EqualTo(1));
}

By setting the return from the controller method to dynamic we avoid the need to explicitly call ExecuteAsync and using dynamic to access the content makes it easier for us to get hold of the content without the need for any Json wrangling.

At this point if you've created the test in the same assembly as the controller it will pass - success!

But, if you've created your test in a separate project when you run the test you'll get an error thrown when trying to check the id in the assert:

'object' does not contain a definition for 'Content'

If you've used dynamic before chances are you'll have seen this error previously as it occurs anytime you try to access a property that it isn't able to find or access on the type that has been returned.

At this point you might be scratching your head wondering why since you're able to access the content and you know exactly what should be in it, especially if you debug the test as you'll see the anonymous object created correctly.

As it turns out the reason you get this error is that when c# constructs an anonymous object at runtime it creates it as an internal class and becauses its internal the project holding the tests will not be allowed to access its properties.

To remedy this all you need to do is to go into the WebApi project and add an attribute to its AssemblyInfo.cs:

[assemblyInternalsVisibleTo("insert the name of your test assembly here")]

doing this now allows your test project to see internal classes in your WebApi project, if you run the test again it will pass.

Code

A repo with code for this can be found here if you want to see it in action