Asp.Net Core appsettings tips

I’ve recently had the opportunity to work on a new project where I was able to use Asp.Net Core for the first time. Well, not completely – I’ve contributed to an open source project that has been using .Net Core for some time, when it was called DNX or ASPNET 5. Anyway, the work I did there really was focused on writing code for the application, not configuring the infrastructure.

A lot has changed, but the changes are largely for the better. There are a few things that tripped me up, so I figured I’d write about them here.

AppSettings have gone JSON

This in and of itself isn’t much of a revelation, but I, for one, am glad to have JSON configuration over XML. In the Startup.cs file, appsettings are configured by default as such:

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
        .AddEnvironmentVariables();
    Configuration = builder.Build();
}

Just like before, when we had Web.config, Web.Release.Config, Web.{EnvironmentName}.config, any environment configuration will be applied on top of the rules defined appsettings.json file. So, if you have an appsettings.json file that looks like:

{
    "MyVariable1": "value1",
    "MyVariable2": "value2"
}

and then you define a file appsettings.production.json that looks like:

{
    "MyVariable1": "productionValue",
}

The production file’s value will be used for MyVariable1 when the application is running in a production environment, as expected.

Accessing appsettings

The easiest way to access a value from your appsettings file is to use Configuration.GetValue:

Configuration.GetValue("MyVariable1", "");

The above will retrieve the value for MyVariable1 or an empty string if there is no key found for MyVariable. The nice thing is you don’t get an exception if a key isn’t found, but this could be an issue if you were expecting a key and get the default value instead.

If your appsettings file has nested objects like this:

{
    "Logging": {
        "IncludeScopes": false,
        "LogLevel": {
            "Default": "Debug",
            "System": "Information",
            "Microsoft": "Information"
        }
    }
}

you can retrieve values by using Configuration.GetValue(“Logging:LogLevel:Default”);

Personally, I don’t like to use magic strings – I prefer to use a strongly typed configuration.

Strongly Typed appsettings

Rick Strahl has a very good article about Strongly typed appsettings, but I will cover the basics. In a nutshell, you need to do two steps to make this work:

  1. Create a class that has all of the corresponding properties of your appsettings (or just a subsection of your appsettings, as I will show below)
  2. Wire up your class by calling the services.Configure<T> method In the ConfigureServices method of your Startup.cs class

Let’s use the following appsettings.json file as an example:

{
    "MySettings" : {
        "AdminEmail" : "admin@email.com",
        "ErrorPath" : "/Home/Error"
    }
}

All we need to complete step 1 is to have a corresponding class for these settings. Here is the corresponding example:

public class MySettings
{
    public string AdminEmail { get; set; }
    public string ErrorPath { get; set; }
}

Now, in our Startup.cs class, we can add the following to our ConfigureServices method:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.Configure<MySettings>(Configuration.GetSection($"{nameof(MySettings)}"));
}

That’s it. Now, we can simply inject MySettings into our MVC/WebAPI controller constructors and Web API will be able to inject that dependency for us.

Note that in this example we called Configuration.GetSection and gave it the name of our section/class – if you only listed the keys AdminEmail and ErrorPath at the root of the appsettings file (without any nested objects), you could have done the same by calling just services.Configure<MySettings>(Configuration);

Using appsettings in your Startup.cs class

One gotcha that had me stumped for a little while was trying to use some of my appsettings configurations to provide configurations in my Startup.cs class. The trick here is using the Bind method on configuration. Here is a good example of what I mean: a lot of tutorials and examples will show configuring exception handling as:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseExceptionHandler("/Home/Error");
}

I like to make that route configurable in my appsettings, so here is how to do that:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    var mySettings = new MySettings();
    Configuration.GetSection($"{nameof(MySettings)}").Bind(mySettings);

    app.UseExceptionHandler(mySettings.ErrorPath);
}

Sending Labels to a Thermal Printer using POST Requests

If you’ve ever used a Thermal printer on a website like Fedex.com, you know it can be kind of ugly to get your label to print. As of this writing, FedEx.com still relies on a Java plugin to do the dirty work of sending the data to your locally-connected printer. I have a couple of applications that integrate with FedEx web services and in the past, I too have relied on Java applets (jZebra, which became qzPrint) to do the work of sending a print job via a web browser.

After searching around, I found a better way to do it: use a simple POST request to send to a network printer. This article is what really got me started: Label And Receipt Printing – Printing from Websites part 2

The sample code shows how you can create a simple XmlHttpRequest object and send it the EPL/ZPL you want. Here is a barebones sample:

var zpl = "^XA^PW400^LL200^FO20,20^A0N,30,30^FDTest^FS^XZ"; //some zpl to send to the printer
var zebraPrinterUrl = "http://192.168.0.100/pstprnt"; //ip address of the printer
var request = new XMLHttpRequest();
request.onload = function () {
  //take some action
};
request.onerror = function () {
  //take some action
};
request.open("POST", zebraPrinterUrl, true); 
request.setRequestHeader("Content-Length", zpl.length);
request.send(zpl);

That’s really all there is to it.

A couple of caveats – as the article I linked above notes, CORS can be a bit of a problem. The zebra printer does not return the necessary Access-Control-Allow-Origin header, so I found this to be disruptive when I tried to use the AngularJS (1.0) $http service. Sending the post using $http would result in a response coming back with status 0, which indicates a CORS problem.

Therefore, I ended up using the XMLHttpRequest object in my application, which can significantly impact testability, unless you wrap the instantiation of XMLHttpRequest objects in another component that you inject into your controller or service.

Finally, because I’m using promises and asynchronous requests, I had to make sure that all of the labels being printed complete before resolving or rejecting the promise.

This solution obviously isn’t very scalable, but I do think it provides much greater flexibility than the old way of using java or some browser plugin to do the job.

Changing from Underscore to Lodash

I have read in a few places that lodash is the way to go (over underscore) when it comes to javascript collection manipulation libraries, but I hadn’t gotten around to changing it out on any of my applications using underscore until recently. I also had a conversation with one of the contributors to the excellent moment js library who told me that lodash is the way forward, and there are some posts out there on the internet that suggest this is the way to go.

Doing the upgrade, I did find there are a couple of functions that aren’t supported by lodash that are by underscore. The list I have found is below:

  • pluck (use map instead – it appears this was changed sometime in January with the release of 4.0: http://stackoverflow.com/questions/35136306/what-happened-to-lodash-pluck)
  • any (use some instead – I had used any because it is operates much like the LINQ Any method)

I will add more here as I come across them.