Cms.LightSdk.Sapico.AspNet 10.0.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
EvaluationContextwithout 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 URLOTEL_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:
emailorhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddressroleorhttp://schemas.microsoft.com/ws/2008/06/identity/claims/role- Any custom
user.claim:xyzrule 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.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
ApiKeyandSiteSlugto Quick Start and Configuration Options - Added
PackageReadmeFileso README renders on NuGet/BaGet package page
[0.5.1] — 2025-07-14
Fixed
- README: Added missing
ApiKeyandSiteSlugto the Quick Start code example - README: Added
SiteSlugrow to the Configuration Options table - README: Expanded
ApiKeydescription with where to generate it (Connectors settings) and how it's sent (X-Api-Keyheader)
[0.1.1] — 2026-04-12
Changed
- Include
README.mdandCHANGELOG.mdin NuGet package (Pack=truein .csproj) so consumers see documentation on nuget.org
[0.1.0] — 2026-04-05
Added
AddCmsTargeting()— registersCmsClient, background refresh service, and in-memory cacheAddCmsOpenTelemetry()— configures OpenTelemetry with OTLP exporter and auto-instrumentationUseCmsTargeting()— middleware that auto-injectsEvaluationContextand sets visitor/session cookiesCmsTargetingMiddleware— enriches OpenTelemetry spans with CMS visitor attributesCmsCacheRefreshService— background service for demand-driven cache warmingEvaluationContextFactory.FromHttpContext()— builds full evaluation context from requestHttpContextExtensions.GetCmsContext()— retrieveEvaluationContextin 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
.NET 10.0
- Cms.LightSdk (>= 10.0.0)
- OpenTelemetry.Api (>= 1.15.1)
- OpenTelemetry.Exporter.OpenTelemetryProtocol (>= 1.15.1)
- OpenTelemetry.Extensions.Hosting (>= 1.15.1)
- OpenTelemetry.Instrumentation.AspNetCore (>= 1.15.1)
- OpenTelemetry.Instrumentation.Http (>= 1.15.0)
- ZiggyCreatures.FusionCache (>= 2.6.0)