Skip to main content
net10.0

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

Published
Aug 14, 2026
2mo ago
Total Downloads
3.9K
3,941

Install

dotnet add package Sylin.Koan.Media.Web --version 1.0.0
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;
using Koan.Web.Extensions;

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

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().AsWebApi();

A custom IMediaSource is the equivalent override for a non-Entity source. Zero or several candidates without an override reject host startup with that correction.

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

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.