Relatude Help

The middleware that serves it

Nothing maps an endpoint for these URLs: you add a middleware. It is about thirty lines around TryParseUrlForContent and FileHandler.HandleFileAsync:

public class RelatudeDBMiddleware(RequestDelegate next) {
    public async Task Invoke(HttpContext http, RelatudeDBContext ctx) {
        if (RelatudeDBRuntime.IsReady) {
            // scheme and host included, so domain routing can see them
            var url = http.Request.Scheme + "://" + http.Request.Host
                    + http.Request.Path.Value + http.Request.QueryString;
            // firstInQueue: a browser waits, so this goes ahead of background conversions
            if (ctx.Database.TryParseUrlForContent(url, out var content, firstInQueue: true)) {
                var result = content.Id.Target switch {
                    UrlTarget.Property or UrlTarget.PropertyAdjusted
                        => await FileHandler.HandleFileAsync(http, content.Stream, content.FileName,
                               content.Attachment, content.ContentType, content.Cacheable),
                    UrlTarget.Node or UrlTarget.EmbeddedNode => RenderPage(http, content),
                    _ => null,
                };
                if (result != null) { await result.ExecuteAsync(http); return; }
            }
        }
        await next.Invoke(http);
    }
}

Register it after the static files. A URL manager can own /, so the middleware sees every request that gets that far:

app.UseDefaultFiles();
app.UseStaticFiles();
app.UseMiddleware<RelatudeDBMiddleware>();
app.UseRelatudeDB();

Four things in the middleware matter:

  • Keep the IsReady check. The store opens in the background, and requests that arrive before it is open would throw instead of passing on.
  • Pass scheme and host, not just the path. Domain routing needs the host, and an asset token can be in the query string.
  • Other requests go on to next. TryParseUrlForContent returns false for URLs that are not the store's, so a tampered asset URL ends as a plain 404.
  • Rendering pages is up to you. For UrlTarget.Node and EmbeddedNode you get content.NodeData, and content.Id.CultureCode names the culture the URL matched. Return null to pass the request on to MVC or Razor Pages.

FileHandler answers a Range request with 206 Partial Content when the stream can seek, as a file on local disk can; video and audio players need that. A file in blob storage or in a single-file store is served whole. If your web layer parses URLs before it decides what serves them, call TryParseUrl once and then GetUrlContentAsync(keys), rather than parsing twice.