Anthony Chu Contact Me

Globally Configure Tag Helpers with ASP.NET Dependency Injection

Monday, February 8, 2016

Tag Helpers are simple to create and they help make ASP.NET MVC Razor views extremely readable. Recently I created a Tag Helper that needed to be globally configured with an API key so it can call an API.

In this article I'll discuss how to build a Tag Helper to display a GIF from Giphy; and how to use ASP.NET Core's built-in dependency injection to configure this Tag Helper at application startup.

The Giphy Tag Helper

The Tag Helper is fairly straight-forward. It's a giphy tag with a search-term attribute. Here's an example:

<giphy search-term="superman" />

This will display a random GIF related to the search term "superman". Behind the scenes, the Tag Helper calls the Giphy API to get a random URL based on the search term. We'll need an API key to call the API, so we need a way to globally configure it.

Here's the Tag Helper:

[OutputElementHint("img")]
public class GiphyTagHelper : TagHelper
{
    private GiphyTagHelperConfiguration _config;

    public GiphyTagHelper(GiphyTagHelperConfiguration config)
    {
        _config = config;
    }

    public string SearchTerm { get; set; }

    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        string url = await GetGiphyUrl(_config.ApiKey, SearchTerm);

        if (string.IsNullOrWhiteSpace(url))
        {
            output.SuppressOutput();
        } else
        {
            output.TagName = "img";
            output.TagMode = TagMode.SelfClosing;
            output.Attributes["src"] = url;
        }
    }

    private async Task<string> GetGiphyUrl(string apiKey, string searchTerm)
    {
        // ...
    }
}

The constructor requires a GiphyTagHelperConfiguration object. We'll get ASP.NET's built-in IoC container to provide it at runtime. (more on this later)

The SearchTerm public property will be exposed as a search-term attribute in the Tag Helper.

The core of the Tag Helper is quite simple. It'll use the API key from the config object and the search term to get a GIF URL from Giphy. If a URL is returned, it'll transform the Tag Helper to an img tag and set its src attribute to the URL. If it failed to get a URL, it'll hide the entire element (output.SuppressOutput()).

The Configuration Object and Helper Extension Method

We need to create a simple configuration object to hold settings for the Tag Helper. For now, it'll only have an ApiKey property:

public class GiphyTagHelperConfiguration
{
    public string ApiKey { get; }

    public GiphyTagHelperConfiguration(string apiKey)
    {
        ApiKey = apiKey;
    }
}

The goal is to create a singleton of this class and add it to the IoC container. We'll create an extension method on IServiceCollection to do this:

public static class GiphyTagHelperExtensions
{
    public static void AddGiphyTagHelper (this IServiceCollection services, 
        GiphyTagHelperConfiguration config)
    {
        services.AddSingleton(_ => config);
    }
}

ASP.NET Core Configuration

Now it's time to go into the StartUp class to hook everything up:

public class Startup
{
    private readonly IConfiguration Configuration;

    public Startup()
    {
        var config = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json")
            .AddEnvironmentVariables()
            .Build();
        Configuration = config;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddGiphyTagHelper(new GiphyTagHelperConfiguration(Configuration["giphy:apiKey"]));
        // ...
    }

    public void Configure(IApplicationBuilder app)
    {
        // ...   
    }
}

We're using the AddGiphyTagHelper() extension method to do all the work to set up the IoC container with a GiphyTagHelperConfiguration object containing the API key. The value is pulled out of an appsettings.json file and can be overridden by an environment variable. For more information on how configuration works in ASP.NET Core, see this documentation page.

Now every instance of the GiphyTagHelper will have access to the API key via the configuration object.

The Controller and Razor View

The MVC controller is super simple. The model is a class that contains a SearchTerm property:

public class HomeController : Controller
{
    public IActionResult Index(IndexModel model) => View(model);
}

And the view looks like this:

@model TagHelperDemo.Mvc.Models.IndexModel
@addTagHelper "*, GiphyTagHelper"
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>

    <form asp-controller="Home" asp-action="Index" method="get" asp-antiforgery="false">
        <input asp-for="SearchTerm" /> <button type="submit">Giphy!</button>
    </form>

    <giphy search-term="@Model.SearchTerm" />

</body>
</html>

Here's the Tag Helper in action:

Source Code

And here's the source code:

https://github.com/anthonychu/aspnet-tag-helper-demo