Feel like sharing?

Gravatar.com is awesome. It provides a universal way to access a headshot or avatar via a simple image based URL. If you’re building any kind of web based application that stores or manages user profiles, you should consider integrating with Gravatar.

There are a number of code examples showing how to integrate Gravatar icons in to your .NET Razor/Mvc application, but the approach I prefer implements gravatars as a a tag helper. The best resource I found for a reference implementation is on TechieWeb here:

https://techiesweb.net/2016/10/01/asp-net-core-custom-tag-helper-for-gravatar-image.html

Ultimately there isn’t a lot you need to to configure to use the code but there was one small missing piece of functionality that we thought it needed:

In most my web applications, users are optionally allowed to upload their own head shot which we store and deliver when needed. Only when the user hasn’t provided us with an image do we want to the Gravatar service.

Wouldn’t it be nice if you could pass an existing Url into the Gravatar helper tag and let it manage the logic to determine which image to use?

Luckily, adding this logic was pretty simple:

[HtmlTargetElement("span", Attributes = EmailAttributeName)]
    public class GravatarImageTagHelper : TagHelper
    {
        private const string SizeAttributeName = "image-size";
        private const string EmailAttributeName = "gravatar-email";
        private const string AltTextAttributeName = "alt";
        private const string LocalHeadshotAttributeName = "local-headshot";

        private const string GravatarBaseUrl = "http://www.gravatar.com/avatar.php?";

        [HtmlAttributeName(EmailAttributeName)]
        public string Email { get; set; }

        [HtmlAttributeName(AltTextAttributeName)]
        public string AltText { set; get; }

        [HtmlAttributeName(SizeAttributeName)] public int? Size { get; set; }

        [HtmlAttributeName(LocalHeadshotAttributeName)]
        public string LocalHeadshot { get; set; }

        private string ToGravatarHash(string email)
        {
            var encoder = new UTF8Encoding();
            var md5 = MD5.Create();
            var hashedBytes = md5.ComputeHash(encoder.GetBytes(email.ToLower()));
            var sb = new StringBuilder(hashedBytes.Length * 2);

            for (var i = 0; i < hashedBytes.Length; i++) sb.Append(hashedBytes[i].ToString("X2"));

            return sb.ToString().ToLower();
        }

        private string ToGravatarUrl(string email, int? size)
        {
            if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(email.Trim()))
                throw new ArgumentException("The email is empty.", nameof(email));

            var sb = ToGravatarHash(email);

            var imageUrl = GravatarBaseUrl + "gravatar_id=" + sb;
            if (size.HasValue) imageUrl += "?s=" + size.Value;
            imageUrl += "&d=%2Fimages%2F/default-avatar.jpg";
            return imageUrl;
        }

        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            var str = new StringBuilder();
            var url = "";
            if (!string.IsNullOrEmpty(LocalHeadshot)
                url = LocalHeadshot;
            else
                url = ToGravatarUrl(Email, Size);
            str.AppendFormat(
                "<img id='gravatar'src='{0}' alt='{1}' alt='image' class='rounded-circle' width='{2}' height='{2}' />",
                url, AltText, Size);
            output.Content.AppendHtml(str.ToString());
        }
    }

Now instead of having all your Razor pages that display a gravatar look like this:

@if(!string.IsNullOrEmpty(userProfile.Headshot))
{
    <img src="@userProfile.Headshot" />
}
else
{
<span gravatar-email="@userProfile.Email" image-size="24"></span>
}

you can let the Helper tag do all the work:

<span gravatar-email="@userProfile.Email" local-headshot="@userProfile.Headshot" image-size="24"></span>

Happy head shots!

Feel like sharing?

Last modified: May 18, 2020

Author