Runic.Assets.AspNetCore 0.12.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package Runic.Assets.AspNetCore --version 0.12.1
                    
NuGet\Install-Package Runic.Assets.AspNetCore -Version 0.12.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Runic.Assets.AspNetCore" Version="0.12.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Runic.Assets.AspNetCore" Version="0.12.1" />
                    
Directory.Packages.props
<PackageReference Include="Runic.Assets.AspNetCore" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Runic.Assets.AspNetCore --version 0.12.1
                    
#r "nuget: Runic.Assets.AspNetCore, 0.12.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Runic.Assets.AspNetCore@0.12.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Runic.Assets.AspNetCore&version=0.12.1
                    
Install as a Cake Addin
#tool nuget:?package=Runic.Assets.AspNetCore&version=0.12.1
                    
Install as a Cake Tool

Runic.Assets.AspNetCore

Serve a Runic Assets manifest through ASP.NET Core GET and HEAD endpoints without recreating its media types, cache policy, content lengths, or entity tags.

Install

dotnet add package Runic.Assets.AspNetCore --prerelease

The package targets .NET 10, uses the shared Microsoft.AspNetCore.App framework, and brings Runic.Assets with it. It is currently a preview package. Choose this adapter for ASP.NET Core; choose the core package alone when you only need an asset source or archive.

Map an embedded Vite build

After configuring RunicAssetsDist in the web application's project and building the Vite dist directory, map the source in Program.cs:

using System.Reflection;
using Runic.Assets;
using Runic.Assets.AspNetCore;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

AssetArchiveSource assets = AssetArchive.ReadEmbedded(
    Assembly.GetExecutingAssembly());

app.MapRunicAssetSource(assets);
app.Run();

This serves the Vite entry point at / and /index.html, every manifest path at its exact URL, and the entry point for missing paths without a file extension (such as /settings/profile), so client-side routes survive a reload. A path with a trailing slash, such as /settings/, is not an asset path and is not served.

The endpoint matches only GET and HEAD requests for paths that resolve this way. It runs after the application's own endpoints, including controller routes, and before MapFallback. Other requests reach static files, a fallback or a 404.

With the fallback on, an unknown extensionless GET below the mount, such as /api/typo, returns the entry point with status 200. If the same application serves an API, mount the assets under a prefix or turn the fallback off. Add an optional prefix when assets should live below a route:

app.MapRunicAssetSource(assets, "ui");

/ui and /ui/ both serve the entry point. Relative URLs in the document resolve against / when it is requested as /ui, so build the frontend with an absolute base, such as Vite's base: "/ui/".

The Desktop adapter in Runic.Assets.Desktop resolves paths with the same AssetManifest.TryResolveRequestPath rules and defaults, so one archive routes identically on both hosts. The hosts differ outside the adapters: Runic Desktop answers other methods with 405 and decodes %2F in request paths, while ASP.NET Core leaves both to the application. Pass AssetRoutingOptions to change the rules:

// Only exact manifest paths; the root and unknown paths return 404.
app.MapRunicAssetSource(assets, routing: new AssetRoutingOptions
{
    ServeEntryPointAtRoot = false,
    EnableSinglePageApplicationFallback = false,
});

The adapter accepts any IAssetSource, which opens each asset's descriptor and stream together: AssetArchiveSource, EmbeddedAssetSource, DevelopmentDirectoryAssetSource or your own.

To serve an asset from your own endpoint, resolve it and call the HttpContext extension WriteAssetAsync, which writes the same headers and handles conditional and range requests:

app.MapGet("/download/{name}", async (HttpContext context, string name) =>
{
    if (assets.Manifest.TryGetAsset("downloads/" + name, out AssetDescriptor? asset))
        await context.WriteAssetAsync(assets, asset);
    else
        context.Response.StatusCode = StatusCodes.Status404NotFound;
});

A minimal-API handler can return RunicAssetResults.Asset instead. The path resolves with the same routing rules when the result runs, against the source's current manifest, and a path that does not resolve is 404:

app.MapGet("/app/{**path}", (string? path) => RunicAssetResults.Asset(assets, path ?? ""));

Content-Security-Policy

Set AssetRoutingOptions.ContentSecurityPolicy to send a policy with every HTML asset; scripts, styles and other assets are sent without it. The archive carries no policy, because only the application knows its script and connect origins:

app.MapRunicAssetSource(assets, routing: new AssetRoutingOptions
{
    ContentSecurityPolicy = "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'",
});

MapRunicAssetSource, RunicAssetResults.Asset and the Desktop adapter send it; WriteAssetAsync does not. A value with a line break or another control character throws ArgumentException.

Static files middleware

RunicAssetFileProvider exposes a source as an IFileProvider, for UseStaticFiles, UseDefaultFiles, UseDirectoryBrowser or another consumer of file providers:

var files = new RunicAssetFileProvider(assets);
app.UseDefaultFiles(new DefaultFilesOptions { FileProvider = files });
app.UseStaticFiles(new StaticFileOptions { FileProvider = files });

Its files are the declared assets and its directories are their parents. A file has no physical path, and its last-modified time is the archive format's fixed timestamp (1980-01-01). CreateReadStream waits for the source's OpenAsync: archive, embedded and development-directory sources open at once, and another source blocks the request thread. Watch fires on the next change of a source that implements IAssetSourceChangeNotifier, such as DevelopmentDirectoryAssetSource, whatever the filter.

The static files middleware computes its own entity tags from the length and the timestamp, and applies neither the manifest's cache modes nor a Content-Security-Policy. Prefer MapRunicAssetSource when nothing else needs a file provider.

HTTP behavior

Responses preserve the manifest-owned content type, length, cache-control value, and strong ETag, and include X-Content-Type-Options: nosniff. text/* content types without a charset are sent with ; charset=utf-8, as the Desktop adapter sends them, so one archive gets the same Content-Type on both hosts. HEAD returns the same headers without a body. Matching If-None-Match values receive 304 Not Modified. Unknown and invalid paths that the routing options do not send to the entry point return 404; the adapter never serves files outside the manifest.

Documentation and support

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.13.0 0 10/29/2026
0.12.1 257 10/27/2026
0.12.0 161 10/27/2026
0.11.0 1,316 10/25/2026
0.10.0 2,803 10/22/2026
0.9.1 398 10/21/2026
0.9.0 125 10/21/2026
0.8.0-preview.6 444 10/19/2026
0.8.0-preview.5 145 10/17/2026
0.8.0-preview.4 394 10/17/2026
0.8.0-preview.3 399 10/15/2026
0.8.0-preview.2 83 10/15/2026
0.8.0-preview.1 142 10/15/2026
0.7.0-preview.9 331 10/14/2026
0.7.0-preview.8 84 10/12/2026
0.7.0-preview.7 60 10/12/2026
0.7.0-preview.6 354 10/9/2026
0.7.0-preview.5 127 10/9/2026
0.7.0-preview.4 76 10/8/2026
0.7.0-preview.3 65 10/8/2026
Loading failed