29

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.

5 Answers 5

40

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"
        });
Sign up to request clarification or add additional context in comments.

7 Comments

Downvoting because this adds a security risk as explained in the ASP Core docs. The accepted answer is the best way to "whitelist" files to be served without opening up all file types.
If you are concerned about security you can either reduce the scope of the served file with this option with a specific PhysicalFileProvider, or implement your own IFileProvider as the StaticFileOptions.FileProvider property.
Warning Enabling ServeUnknownFileTypes is a security risk. It's disabled by default, and its use is discouraged.
Just what I needed thanks. This is development work, i dont need to worry about deep security. If you want your default to be binary files use "application/octet-stream"
The Core docs don't actually explain how it's a security risk, they just state it. On its own I don't see how it's a risk.
|
24

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.

4 Comments

Lol, this is pretty much exactly what I gave up and reverted to - although it feels somewhat wrong, but it does work!
If it's stupid but it works, then it ain't stupid! :)
Like this you don't event need an actual file in the wwwroot. I've had the same problem with the Apple file - I've put the credentials into appsettings and generated json in code.
How locally works and at server level not worked. does server block it?
9

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));

2 Comments

Best answer! No security risks like allowing all unknown static filetypes and no overhead by creating a controller for returning a simple json file.
This somehow didn't work for me in .NET 6. I had to use app.UseRewriter(new RewriteOptions().AddRewrite("^apple-app-site-association$", "/apple-app-site-association.json", true));
6

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)

Comments

1

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;
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.