.NET Core

2 posts

Cronos - ASP.NET Core Application

Cronos - ASP.NET Core Application

I wrote an asp.net core application. It's live at https://cronos.frenetik.io. It is an app that helps you create a chronological playlist based on an artist for Spotify. The source is up at https://github.com/mikeruhl/cronos. I won't go into much detail here since the readme covers a lot on github.

Just some backstory: A friend had posted a request on facebook that Spotify should offer this ability. I figured it would be a fun project so I started working on it in spare time. 1 month later, I had a working application. The process went very smooth due to a library I found at https://github.com/dotnetfan/FluentSpotifyApi. It's very well written and I really appreciate the work that went into it. I had started futzing around with a proof of concept using RestSharp when I figured someone else had already done the dirty work. Sure enough, this great library fell into my lap after a quick google search.

You'll see from the source, I put some boilerplate session handling in CronosBaseController.cs. I liked the way that turned out, even though I didn't use it for any other controllers. I'm getting into the habit of hiding code that won't change and is on a different scope from the rest. I found it easier to work in HomeController.cs without that session code.

The code could use some refactoring. It also needs tests. I released it in a beta sense, and based on the fanfare (avg 1 user per day) I can guess it's not going to get much use. So I'll leave it where it stands and move on to the next project.

Merging IOptions<T> settings from multiple sources

.NET Core rocks for so many reasons. One thing I've been using recently is demoing updates and real-world debugging via Azure and then deploying to my VPS in a Docker container somewhere else. I don't generate enough traffic to make an Azure web service more affordable than a VPS, which is why I don't run everything from the cloud. So, QA is a WebApp running on Azure in a cheap tier, which I use my credits from my MSDN subscription for, then my production is in Docker running from the same box as this blog.

.NET Core makes configuration between these two sources so simple. If you know how to use environment variables in docker, in an OS, and in a cloud service (like Azure), then you can deploy any of those ways without changing anything at build or run time (aside from initial set-up).

Here's the options class I'm dealing with:

public class EmailSettings
{
    public string MailGunApiKey { get; set; }
    public string ApiBaseUri { get; set; }
    public string From { get; set; }
    public string Domain { get; set; }
    public string RecaptchaApiKey { get; set; }
    
    public bool IsValid()
    {
        return !string.IsNullOrEmpty(MailGunApiKey) &&
            !string.IsNullOrEmpty(ApiBaseUri) &&
            !string.IsNullOrEmpty(From) &&
            !string.IsNullOrEmpty(Domain) &&
            !string.IsNullOrEmpty(RecaptchaApiKey);
    }
}

Super simple, I have a form on a web page to email me. I implemented ReCaptcha v2 to cut down on spam and I use the MailGun API to send the email. ApiBaseUri is the base API URL for MailGun, From is the email I'm sending to (me), Domain is the domain I'm sending from, and the two ApiKey properties are for each API, respectively.

The hurdle I had to jump across yesterday dealt with IOptions<T>. Typically, you can easily deserialize an object from json into your settings object with a one-liner. Then you can use Dependency Injection to access it in a controller. Here's an example:

//Startup.cs

public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));
    }

This works great when all of your properties in that Options class are in one location with equal security requirements. As you can see, I created a class, though, where some properties could be stored in a file and others (like the private api keys) needed to be stored as environment variables, to avoid being committed to source. What I wanted was the portability and ease to store many config variables in a file with the security of keeping some out of source and directly on the host.

So, I came up with a solution of blending sources into one class so that they would be available as one. In order to load my api keys separately, this is what I added in place of the statement noted above:

public void ConfigureServices(IServiceCollection services)
{
//...
    services.Configure<EmailSettings>(options=> {

        options.ApiBaseUri = Configuration["EmailSettings:ApiBaseUri"];
        options.From = Configuration["EmailSettings:From"];
        options.Domain = Configuration["EmailSettings:Domain"];
        options.MailGunApiKey = Configuration["EmailSettings:MailGunApiKey"];
        options.RecaptchaApiKey = Configuration["EmailSettings:RecaptchaApiKey"];
        if (string.IsNullOrEmpty(options.MailGunApiKey) &&  Configuration.GetValue<string>("MAILGUN_API_KEY") != null)
        {
            options.MailGunApiKey = Configuration.GetValue<string>("MAILGUN_API_KEY");
        }
        if (string.IsNullOrEmpty(options.RecaptchaApiKey) && Configuration.GetValue<string>("RECAPTCHA_API_KEY") != null)
        {
            options.RecaptchaApiKey = Configuration.GetValue<string>("RECAPTCHA_API_KEY");
        }

    });
    //...
}

From this I'm getting exactly what I want from each "source". If I want to set variables in my file, I can and they will populate and not be overwritten by environment variables. Otherwise, it checks for environment variables too. I listed out all of these properties to show that you can also mix and match what you grab from JSON on a per-property level.

To finish this up, I call the EmailSetting.IsValid() method in my page generation to decide whether to build the html for the email form if one of these things is missing. That way, I won't have unnecessary broken calls to my API and it's also a quick way to see if something is missing. Combining this method in production with UserSecrets in Development is an excellent way to manage your dev and prod configs separately and securely.

More info on Configuration in .NET Core 2 can be found here.