Matt Roberts
Matt Roberts

Reputation: 26907

asp.net core - How to serve static file with no extension

ASP.NET Core hapily serves up files from the wwwroot folder based on the mime type of the file. But how do I get it serve up a file with no extension?

As an example, Apple require that you have an endpoint in your app /apple-app-site-association for some app-intergration. If you add a text file called apple-app-site-association into your wwwroot it won't work.

Some things I've tried:

1) Provide a mapping for when there's no extension:

var provider = new FileExtensionContentTypeProvider();
provider.Mappings[""] = "text/plain";
app.UseStaticFiles(new StaticFileOptions
            {
                ContentTypeProvider = provider
            });

2) Adding an app rewrite:

var options = new RewriteOptions()
.AddRewrite("^apple-app-site-association","/apple-app-site-association.txt", false)

Neither work, the only thing that does work is a .AddRedirect which I'd rather not use if possible.

Upvotes: 29

Views: 14461

Answers (5)

angularsen
angularsen

Reputation: 8678

AspNetCore does not serve unknown file types for security reasons, but it should generally be safe to do for files in the .well-known folder.

The problem is that Apple requires the correct MIME type for files without extensions, so we must explicitly configure those.

Solution

  1. Allow serving unknown file types (no extension) from .well-known folder.
  2. Add files to wwwroot/.well-known folder.
  3. Add a custom IContentTypeProvider to map filenames to MIME type, such as apple-app-site-association file name to application/json MIME type.

Startup.cs

app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "wwwroot", ".well-known")),
    RequestPath = "/.well-known",
    ServeUnknownFileTypes = true,
    ContentTypeProvider = new CustomWellKnownFileContentTypeProvider(),
});

CustomWellKnownFileContentTypeProvider.cs

/// <summary>
///     Custom file extension content type provider for serving certain extension-less files in .well-known folder.
///     <list type="bullet">
///         <item>apple-app-site-association - application/json - Apple Universal Link association</item>
///     </list>
/// </summary>
public class CustomWellKnownFileContentTypeProvider : IContentTypeProvider
{
    private readonly FileExtensionContentTypeProvider _defaultProvider = new();

    public bool TryGetContentType(string subpath, out string contentType)
    {
        // Custom handling of files without file extensions.

        // Apple Universal Link association, requires the correct MIME type set.
        if (subpath.EndsWith("apple-app-site-association", StringComparison.OrdinalIgnoreCase))
        {
            contentType = "application/json";
            return true;
        }

        // Fallback to default provider, based on file extension.
        if (_defaultProvider.TryGetContentType(subpath, out string? extContentType))
        {
            contentType = extContentType;
            return true;
        }

        contentType = string.Empty;
        return false;
    }
}

Upvotes: 1

Christina P.
Christina P.

Reputation: 61

I think the easiest way is to add the apple-app-site-association file in a .well-known folder in the root folder of the application as described here: [https://developer.apple.com/documentation/safariservices/supporting_associated_domains] and then allow access to it from your code, like this (Startup.cs):

 // to allow access to apple-app-site-association file
app.UseStaticFiles(new StaticFileOptions()
{
    FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, @".well-known")),
    RequestPath = new PathString("/.well-known"),
    DefaultContentType = "application/json",
    ServeUnknownFileTypes = true,
});

Tested in AWS Serverless Application (.NET Core 3.1)

Upvotes: 6

Gabriel Luci
Gabriel Luci

Reputation: 41008

Rather than fighting with static files, I think you'd be better off just creating a controller for it:

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using System.IO;

namespace MyApp.Controllers {
    [Route("apple-app-site-association")]
    public class AppleController : Controller {
        private IHostingEnvironment _hostingEnvironment;

        public AppleController(IHostingEnvironment environment) {
            _hostingEnvironment = environment;
        }

        [HttpGet]
        public async Task<IActionResult> Index() {
            return Content(
                await File.ReadAllTextAsync(Path.Combine(_hostingEnvironment.WebRootPath, "apple-app-site-association")),
                "text/plain"
            );
        }
    }
}

This assumes your apple-app-site-association file is in your wwwroot folder.

Upvotes: 24

Serge S
Serge S

Reputation: 91

An easier option may be to put a file with a proper extension on the server, and then use URL rewrite as follows.

app.UseRewriter(new RewriteOptions()
    .AddRewrite("(.*)/apple-app-site-association", "$1/apple-app-site-association.json", true));

Upvotes: 9

sinofis
sinofis

Reputation: 596

Adding an alternative solution. You have to set ServeUnknownFileTypes to true and after that set the default content type.

        app.UseStaticFiles(new StaticFileOptions
        {
            ServeUnknownFileTypes = true,
            DefaultContentType = "text/plain"
        });

Upvotes: 40

Related Questions