Skip to main content
net10.0

Recipe-driven HTTP rendering and Entity-backed media serving for Koan.

Published
Sep 9, 2026
1mo ago
Total Downloads
3.9K
3,921

Install

dotnet add package Sylin.Koan.Media.Web --version 1.0.52
ProductVersions
.NET
net10.0

README

Sylin.Koan.Media.Web

Serve an application's MediaEntity<T> originals and named recipes through controller-owned HTTP routes. The reference supplies discovery, conditional/cache headers, bounded request parsing, format negotiation, and optional persisted derivatives.

dotnet add package Sylin.Koan.Media.Web

Smallest meaningful result

using Koan.Core;
using Koan.Media;
using Koan.Media.Abstractions.Recipes;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKoan();

var app = builder.Build();
await app.RunAsync();

public sealed class Photo : MediaEntity<Photo>;

public static class PhotoRecipes
{
    [MediaRecipe("card", Description = "320px JPEG card")]
    public static MediaRecipe Card() => MediaRecipe.New()
        .Resize(width: 320)
        .EncodeAs("jpeg", Quality.Web)
        .Build();
}

One concrete MediaEntity<T> is selected automatically. The application still needs Data and Storage providers for its Photo Entity; no registration helper or application rendering controller is required.

When the application has several media Entity types, select the one that owns the bare route deliberately:

builder.Services.AddMediaSource<Photo>();
builder.Services.AddKoan();

A custom IMediaSource is the equivalent override for a non-Entity source. Several candidates without an override reject host startup with that correction. No candidates at all is not one: Media Web composes inertly and the routes simply have no source until a MediaEntity<T> exists.

Routes

  • GET /media/{id} β€” original bytes;
  • HEAD /media/{id} β€” original validators, content type, and length without a body;
  • GET /media/{id}/{recipe} β€” named recipe or supported format shortcut;
  • HEAD /media/{id}/{recipe} β€” recipe-result metadata without a body;
  • GET /media/{id}/{recipe}?w=...&q=... β€” allowlisted overrides;
  • GET /media/recipes β€” materialized catalog and shortcuts; and
  • GET /media/recipes/{name}?as=appsettings β€” canonical recipe configuration.

MediaEntitySource<TEntity> resolves the source through the Entity data path before consulting a derivative, so active tenancy and access axes gate both cold and warm requests.

Originals and recipe results backed by seekable streams support standard byte ranges, including 206, Content-Range, Accept-Ranges: bytes, and length-bearing 416 responses. A non-seekable custom source remains valid for complete GET/HEAD delivery, advertises Accept-Ranges: none, and rejects a requested range correctively rather than silently returning the full body.

Current limits

  • one bare /media/{id} route has one IMediaSource; applications with multiple media Entity types must choose one source or own a discriminating custom source;
  • the default source buffers a completed render before persisting its derivative;
  • derivative writes are best-effort and source deletion does not automatically reclaim derivative storage;
  • there is no upload-time prewarm, signed route, or content-addressed route shape; and
  • routes are currently fixed under /media.

See the Media reference and technical companion.

Changes from Previous Version

No structural changes between v1.0.47 and v1.0.52.

Adoption Assessment

Assess this package against its supported frameworks, license, maintenance signals and dependency graph before making it a project standard.

Compatibility metadata lists 1 target framework; confirm the exact target used by your application.The published metadata declares 15 direct dependency entries across framework groups. Inspect the resolved dependency graph for your target framework.

Related Guides & Workflows

Learn how to evaluate, integrate, or audit this package in your .NET project.