Cms.LightSdk.Sapico.AspNet 10.3.0

Cms.LightSdk.Sapico.AspNet

ASP.NET Core integration for the Sapico CMS Content Targeting engine. This package provides middleware, service registration, and OpenTelemetry integration for consuming CMS resources with edge resolution in ASP.NET 10 applications.

Features

  • Automatic EvaluationContext — extracts visitor/session IDs, UTM params, referrer, user-agent, geo-location, and authenticated user info from each request
  • Cookie Management — sets _cms_vid (persistent visitor ID) and _cms_sid (session ID with returning-visitor flag)
  • OpenTelemetry Enrichment — automatically tags every span with cms.visitor_id, cms.session_id, cms.tenant_slug, cms.is_returning
  • Background Cache Warming — demand-driven refresh of resources that have been accessed
  • Edge Resolution — local rule evaluation using EvaluationContext without server round-trips

Installation

dotnet add package Cms.LightSdk.Sapico.AspNet --version 10.0.*

Quick Start

1. Register Services

using Cms.LightSdk.Sapico.AspNet;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCmsTargeting(opts =>
{
    opts.BaseUrl = builder.Configuration["CmsApi:BaseUrl"] ?? "https://cms.sapico.me";
    opts.TenantSlug = builder.Configuration["CmsApi:TenantSlug"] ?? "my-tenant";
    opts.SiteSlug = builder.Configuration["CmsApi:SiteSlug"] ?? "default";
    opts.ApiKey = builder.Configuration["CmsApi:ApiKey"];  // from Connectors settings
    opts.DefaultCacheDuration = TimeSpan.FromMinutes(30);

    // Optional: per-resource cache overrides
    opts.CachePolicy("homepage", TimeSpan.FromMinutes(5));
    opts.CachePolicy("product-*", TimeSpan.FromHours(1));
});

2. Add Middleware

var app = builder.Build();

app.UseCmsTargeting();  // Must come before your endpoints

3. Inject and Use CmsClient

app.MapGet("/", async (CmsClient cmsClient, HttpContext ctx) =>
{
    var evalCtx = ctx.GetCmsContext();
    var resource = await cmsClient.GetResolvedResourceAsync("homepage", evalCtx);

    // resource.Archetypes now contain only the variants that matched targeting rules
    return Results.Ok(resource);
});

Configuration Options

CmsClientOptions

Property Type Default Description
BaseUrl string string.Empty Base URL of the CMS API (required)
TenantSlug string string.Empty Tenant identifier (required)
SiteSlug string string.Empty Site identifier within the tenant (required)
ApiKey string? null API key for authenticated access — generate one in the CMS dashboard under Connectors settings. Sent as X-Api-Key header on every request.
DefaultCacheDuration TimeSpan 1 day Default TTL for cached resources
FileCachePath string? null Optional disk cache directory for offline resilience

Cache Policies

opts.CachePolicy("homepage", TimeSpan.FromMinutes(5));      // exact slug
opts.CachePolicy("product-*", TimeSpan.FromHours(1));      // wildcard prefix
opts.CachePolicy("*", TimeSpan.FromDays(7));               // global fallback

Per-slug policies take precedence over DefaultCacheDuration. Wildcards (*) match any slug ending with the pattern.

OpenTelemetry

// Optional: add OpenTelemetry with custom service name
builder.Services.AddCmsOpenTelemetry("MySite");

OpenTelemetry exports to OTLP using environment variables:

  • OTEL_EXPORTER_OTLP_ENDPOINT — OTLP collector URL
  • OTEL_EXPORTER_OTLP_HEADERS — auth headers (e.g., api-key=xxx)

Both ASP.NET Core and HttpClient instrumentation are auto-configured.

HTTP Cookies

The middleware manages two cookies:

Cookie Purpose Lifetime
_cms_vid Persistent visitor ID (anonymous) 365 days
_cms_sid Session ID + returning-flag Browser session

The session cookie value encodes the returning status:

  • First visit: plain GUID (e.g., abc123)
  • Returning visit: r: prefix (e.g., r:abc123)

This allows IsReturningVisitor to persist across all requests in the session.

EvaluationContext

The EvaluationContext captures the full request context for edge resolution:

public class EvaluationContext
{
    public string? Utm { get; set; }                    // ?utm query param
    public string? Location { get; set; }               // X-Geo-Location header or ?loc
    public string? Referrer { get; set; }               // Referer header
    public string? UserAgent { get; set; }              // User-Agent header
    public string? VisitorId { get; set; }              // _cms_vid cookie
    public string? SessionId { get; set; }              // _cms_sid (without r: prefix)
    public bool IsReturningVisitor { get; set; }        // true if session cookie has r: prefix
    public DateTimeOffset Now { get; set; }             // evaluation timestamp
    public Dictionary<string, string> Cookies { get; }  // all request cookies
    public Dictionary<string, string> QueryParams { get; }  // all query params
    public Dictionary<string, string> Headers { get; }  // all request headers

    // Authenticated user (if any)
    public string? UserEmail { get; set; }
    public string? UserDomain { get; set; }
    public List<string> UserRoles { get; set; } = new();
    public Dictionary<string, string> UserClaims { get; set; } = new();

    public string? GetAttributeValue(string attribute);  // resolves rule attribute paths
}

Access it in endpoints:

app.MapGet("/", (HttpContext ctx) =>
{
    var evalCtx = ctx.GetCmsContext();  // extension method
    var visitorId = evalCtx?.VisitorId;
    var userEmail = evalCtx?.UserEmail;
    var isReturning = evalCtx?.IsReturningVisitor;
});

Targeting Attributes (Rule Clauses)

The EvaluationContext.GetAttributeValue() method resolves attribute paths used in rule clauses:

Attribute Source
utm ?utm query param
loc X-Geo-Location header or ?loc
source Referer header
session.id SessionId
session.returning "true" or "false"
user.email Authenticated user's email claim
user.domain Domain part of email
user.role:* Matches any user role against wildcard pattern
user.claim:xyz Specific claim by type
cookie:name Request cookie by name
header:name Request header by name (case-insensitive)
query:name Query parameter by name

CmsClient Methods

GetResourceAsync

Fetches a resource by slug (raw, no edge resolution):

CmsResource? resource = await cmsClient.GetResourceAsync("homepage");

Returns null if not found or API unavailable. Uses cache-aside with stale-if-error.

GetResolvedResourceAsync

Fetches and applies edge resolution locally:

CmsResource? resource = await cmsClient.GetResolvedResourceAsync("homepage", evalCtx);

The returned resource contains only archetypes whose rules matched the context (or have no rules).

GetBestMatchAsync

Tries multiple slug variants and returns the first matching variant:

CmsResource? resource = await cmsClient.GetBestMatchAsync(
    new[] { "homepage-de", "homepage-nl", "homepage" },
    evalCtx
);

Order matters — most specific variants first. Falls back to the first resource without rules if no targeted variant matches.

TrackAccess

Manually mark a slug as accessed to trigger background cache refresh:

CmsCacheRefreshService.TrackAccess("homepage");

The background service polls every ~5 minutes and refreshes all tracked slugs.

Middleware Order

UseCmsTargeting() should be placed early in the pipeline, before endpoints that read EvaluationContext:

app.UseCmsTargeting();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/", ...);

Advanced Usage

Custom Cookies & Headers

All request cookies and headers are automatically captured into EvaluationContext. Access them directly or via GetAttributeValue:

// In a rule: cookie:ab_test_variant == "v2"
var variant = evalCtx.GetAttributeValue("cookie:ab_test_variant");

// Direct access
var allCookies = evalCtx.Cookies;

File Cache (Offline Resilience)

Enable disk persistence to serve cached resources if the API is down:

builder.Services.AddCmsTargeting(opts =>
{
    opts.BaseUrl = "https://cms.sapico.me";
    opts.TenantSlug = "my-tenant";
    opts.FileCachePath = Path.Combine(
        builder.Environment.ContentRootPath, "cms-cache");
});

Resources are saved as {slug}.json files and loaded on startup.

Combining with Authentication

The SDK extracts user claims from HttpContext.User. Ensure authentication runs before the CMS middleware:

app.UseAuthentication();
app.UseCmsTargeting();  // after auth so User is populated

Claims used by targeting rules:

  • email or http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
  • role or http://schemas.microsoft.com/ws/2008/06/identity/claims/role
  • Any custom user.claim:xyz rule looks up the claim by type

ASP.NET 10 Compatibility

This package targets net10.0 and requires:

  • ASP.NET Core 10.0
  • .NET 10.0 SDK or later

Schema

Archetypes are stored as JSON with a $type discriminator:

{
  "$type": "catalog",
  "title": "Homepage Hero",
  "body": "Welcome..."
}

The rules archetype uses clauses for targeting:

{
  "$type": "rules",
  "clauses": [
    { "attribute": "user.role", "pattern": "admin", "negate": false },
    { "attribute": "utm", "pattern": "summer_sale", "negate": false }
  ]
}

All clauses must match (AND logic). negate: true inverts the match.

Troubleshooting

Issue Cause Fix
GetCmsContext() returns null UseCmsTargeting() not called or called after endpoint Move app.UseCmsTargeting() before endpoint mapping
Visitor ID changes on every request Cookies disabled in browser Check browser cookie settings; cookies must be enabled
No user email in EvaluationContext User not authenticated or missing email claim Ensure authentication runs first; verify claim types
Rules not matching Wrong attribute path or case sensitivity Attribute paths are case-insensitive; use GetAttributeValue() to debug
Cache not warming TrackAccess() never called Ensure resources are requested at least once (demand-driven)

License

MIT — See repository for details.

Showing the top 20 packages that depend on Cms.LightSdk.Sapico.AspNet.

Packages Downloads
Cms.LightSdk.Sapico.AspNet.Mvc
MVC and Razor Pages helpers for the Sapico CMS SDK. Provides GetContentAsync extension methods for Controller and PageModel.
7
Cms.LightSdk.Sapico.AspNet.Mvc
MVC and Razor Pages helpers for the Sapico CMS SDK. Provides GetContentAsync extension methods for Controller and PageModel.
4
Cms.LightSdk.Sapico.AspNet.RazorPages
Razor Pages and MVC controller helpers for the Sapico CMS SDK. Provides GetContentAsync extension methods for PageModel and Controller.
3

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

Added

  • n/a

Changed

  • n/a

Fixed

  • n/a

Removed

  • n/a

[10.3.0] — 2026-04-14

Fixed

  • Updated dependency on Cms.LightSdk to 10.2.0

[10.2.0] — 2026-07-17

Fixed

  • Remove duplicate X-Api-Key header and BaseAddress setup from AddHttpClient factory in AddCmsTargeting; CmsClient constructor is now the single source of truth

[10.1.0] — 2026-07-16

Changed

  • API routes restructured to RESTful tenant-scoped paths (/api/tenants/{tenantSlug}/resources, /api/tenants/{tenantSlug}/datasources, etc.)
  • SiteSlug now included in all resource API responses
  • CmsClient.GetResourceAsync updated to use new tenant-scoped route

[10.0.0] — 2025-07-14

Changed

  • Version scheme now follows ASP.NET major version for consistency (10.0.x = .NET 10)
  • Added PackageIcon, PackageReleaseNotes, and enriched NuGet metadata

Fixed

  • README: Added missing ApiKey and SiteSlug to Quick Start and Configuration Options
  • Added PackageReadmeFile so README renders on NuGet/BaGet package page

[0.5.1] — 2025-07-14

Fixed

  • README: Added missing ApiKey and SiteSlug to the Quick Start code example
  • README: Added SiteSlug row to the Configuration Options table
  • README: Expanded ApiKey description with where to generate it (Connectors settings) and how it's sent (X-Api-Key header)

[0.1.1] — 2026-04-12

Changed

  • Include README.md and CHANGELOG.md in NuGet package (Pack=true in .csproj) so consumers see documentation on nuget.org

[0.1.0] — 2026-04-05

Added

  • AddCmsTargeting() — registers CmsClient, background refresh service, and in-memory cache
  • AddCmsOpenTelemetry() — configures OpenTelemetry with OTLP exporter and auto-instrumentation
  • UseCmsTargeting() — middleware that auto-injects EvaluationContext and sets visitor/session cookies
  • CmsTargetingMiddleware — enriches OpenTelemetry spans with CMS visitor attributes
  • CmsCacheRefreshService — background service for demand-driven cache warming
  • EvaluationContextFactory.FromHttpContext() — builds full evaluation context from request
  • HttpContextExtensions.GetCmsContext() — retrieve EvaluationContext in endpoints
  • Wildcard cache policy support (CachePolicy("slug-*", duration))
  • File-based cache persistence (optional via FileCachePath)
  • Stale-if-error cache fallback on API failures

Fixed

  • n/a

Version Downloads Last updated
10.8.0 9 04/20/2026
10.7.0 5 04/20/2026
10.6.0 35 04/18/2026
10.5.0 4 04/18/2026
10.4.0 43 04/16/2026
10.3.0 24 04/14/2026
10.2.0 4 04/12/2026
10.1.0 4 04/12/2026
10.0.0 5 04/12/2026
0.5.1 5 04/12/2026
0.5.0 7 04/12/2026