TIL: mime types and static assets in Umbraco
Tags: umbraco
Published: 2022-10-21 08:37:29

A few months ago I [wrote a post](https://dev.to/cultiv/umbraco-discord-wrap-up-week-18-c2j) about learnings from the [Umbraco Discord](https://discord.gg/umbraco) that week and then.. it went quiet 😅

With the best of intentions to keep that series going, it turns out there's just not enough hours in the day! I have ideas though to open up the information from Discord more but that will have to be bit more long term. 

In the mean time, we did go down a fun exploration path with [Dean Leigh](https://twitter.com/deanleigh) yesterday thinking about adding custom MIME types to Umbraco and overriding existing ones if needed. 

[![Screnshot of a Discord message where Dean is asking how to change MIME types](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bmvw1ix2av7tzd1qyp84.png)](https://discord.com/channels/869656431308189746/882984410432012360/1032544330591785051)

This [turned into a thread](https://discord.com/channels/869656431308189746/1032551319648612374) where explored the actual needs and some solutions. 

## `Program.cs` vs `Startup.cs`

First off, I understand the confusion, Umbraco 9 was built on .net 5 and that required us to have `Startup.cs` and `Program.cs`. 

In .net 6, Microsoft consolidated the two into `Program.cs` and updated all of their documentation to that effect. We're not ready yet in Umbraco to do the same, so there will be some confusion for a while. 

So for now, in Umbraco you're encouraged never to touch `Program.cs` and only ever make changes to `Startup.cs`.

## Mime types

As it turns out, Dean's original question was about trying to figure out how to host and serve the new(-ish) `avif` format, [an image format with really great compression](https://jakearchibald.com/2020/avif-has-landed/). 

![An image showing the difference in image quality between jpg and avif formats](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0k41ors60dxbizytnjh6.jpg) <small>Image credit: [Jake Archibald](https://jakearchibald.com/2020/avif-has-landed/)</small>

Since [.net doesn't yet support avif](https://github.com/dotnet/aspnetcore/issues/39984) and neither does [ImageSharp](https://github.com/SixLabors/ImageSharp/discussions/1320), files of this type just didn't have a MIME type available and trying to use them would result in a 404 error.

I remembered seeing an example recently to help [cache static assets](https://our.umbraco.com/documentation/Tutorials/Cache-Static-Assets/). These are specifically listing file types that ImageSharp doesn't touch. If you added something like `jpg` in that list, the request would be cached very early and never hit the ImageSharp middleware. 

Adding/changing MIME types is pretty similar to adding cache control headers, so I played with that code to see how far we could get. 

As it turns out, I was trying my code on `webp` files first, changing their MIME type from `image/webp` to `image/text` to see if it would work. It didn't! I was close though and with [some great help from my colleague Ronald](https://twitter.com/rgmbarendse) I understood that I was only changing the MIME type for .net and then the next thing kicked in: ImageSharp. When ImageSharp sees a request for a `webp` file, it reads the metadata from the file and sets the MIME type (correctly!) back to `image/webp`. So after a bit of experimenting we came up with the following code:

⚡⚠️ **danger danger, this messes with the default webp content type, don't use in production**⚡⚠️

```csharp
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.StaticFiles;
using SixLabors.ImageSharp.Web;
using SixLabors.ImageSharp.Web.Middleware;
using Umbraco.Cms.Core.Composing;

public class ContentTypeComposer : IComposer
{
    public void Compose(IUmbracoBuilder builder)
    {
        var contentTypeProvider = new FileExtensionContentTypeProvider();
        contentTypeProvider.Mappings[".webp"] = "image/text";

        builder.Services.Configure<StaticFileOptions>(options => options.ContentTypeProvider = contentTypeProvider);
        builder.Services.Configure<ImageSharpMiddlewareOptions>(options =>
        {
            var prepareResponseAsync = options.OnPrepareResponseAsync;
            options.OnPrepareResponseAsync = async (context) =>
            {
                var formatUtilities = context.RequestServices.GetRequiredService<FormatUtilities>();
                if (formatUtilities.TryGetExtensionFromUri(context.Request.GetDisplayUrl(), out var extension) &&
                    contentTypeProvider.TryGetContentType('.' + extension, out var contentType))
                {
                    context.Response.ContentType = contentType;
                }

                await prepareResponseAsync(context);
            };
        });
    }
}
```

Cool, that worked!

![Screenshot of the Edge browser's network tab showing that the MIME type has updated](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n8gxsjukpncu73766a8v.png)

One caveat though, as you can see in the code above, this is updating the Mappings for an existing content type, and as we learned earlier, `avif` is not supported in .net yet and therefore there is no existing content type. We also don't have to think about ImageSharp since it doesn't support `avif` yet either.

So to make `avif` get a proper MIME type, the code needed to change a bit:

```csharp
using Microsoft.AspNetCore.StaticFiles;
using Umbraco.Cms.Core.Composing;

public class ContentTypeComposer : IComposer
{
    public void Compose(IUmbracoBuilder builder)
    {
        var contentTypeProvider = new FileExtensionContentTypeProvider();
        if (!contentTypeProvider.Mappings.ContainsKey(".avif"))
        {
            contentTypeProvider.Mappings.Add(".avif", "image/avif");
        }

        builder.Services.Configure<StaticFileOptions>(options => options.ContentTypeProvider = contentTypeProvider);
    }
}
```

Here we added the MIME type if the mapping doesn't yet exist, if it ever starts to exist in the future we assume it gets the correct mapping anyway. 

## Bonus

If you want to learn more about headers and CORS, my colleague Warren has [a great Hack Make Do video on this topic](https://www.youtube.com/watch?v=Ju_BgmgKvSY) as well! 

{% embed https://www.youtube.com/watch?v=Ju_BgmgKvSY %}

## Conclusion

And there we have it, now we can add a MIME type to any unknown file format and we learned at the same time how to override MIME types for ImageSharp. Plus.. now that we know how to hook into the ImageSharp middleware options, we could probably think of more fun things to do in there as well.