Consommation du CMS headless (point critique)
Les articles/auteurs proviennent du CMS headless accm-content-headless (Delivery API v2). Le defi : les payloads JSON ne se deserialisent PAS directement en BlockGridItem/BlockListItem/RteContent, car ces types dependent de IPublishedElement, Udi, etc., internes a Umbraco. La solution reconstruit des objets Umbraco synthetiques a partir du JSON, pour pouvoir reutiliser les modeles ModelsBuilder et les helpers Razor comme si le contenu etait local.
1. Client API (Services/HeadlessArticleService.cs)
- Appel :
GET delivery/api/v2/content/item/{id}?expand=properties[$all]&fields=properties[$all](base URL =HeadlessCmsApiOptions.BaseUrl). - Transport :
HttpClientExtensions.GetFromApiAsync<T>; mode preview ajoute le headerPreview: true, auth via headerApi-Key; erreurs non-2xx loggees (Serilog, niveau Warning). - Cache :
MemoryCache, TTLHeadlessCmsApiOptions.ArticleDetailCacheSeconds(defaut 300s), cleheadless:article-detail:{id}. Le mode preview bypasse le cache ; seules les reponses OK sont mises en cache. - Deserialisation :
JsonConvert.DeserializeObject<DeliveryApiContentResponse>(json, settings)avec converters enregistres (BlockGridItemConverter,RteContentConverter). HeadlessCmsApiServiceest la facade qui delegue aux sous-services (article/multimedia/author).
2. Converters (Converters/)
- BlockGridItemConverter : lit
{ content, settings, rowSpan, columnSpan, areas }, cree lesIPublishedElementvia la factory, enveloppe dansGuidUdi("element", key), traite recursivement lesareasimbriquees. - RteContentConverter : lit
{ markup, blocks }, cree unIPublishedElementpar block, enveloppe dansBlockListItem, renvoieRteContent.Blocks. - PublishedTypeResolver :
GetContentTypeOrThrow(alias)resout l'alias via le snapshot publie local (le content type doit exister localement), sinon exception (fail-fast). - HeadlessPublishedElement / HeadlessPublishedProperty : implementations legeres d'
IPublishedElement/IPublishedProperty; la valeur est evaluee paresseusement via unFunc<object?>.
3. Factory (Factories/HeadlessElementFactory.cs)
CreateFromDelivery(JObject content) :
- Lit
contentTypeetid, resout le content type viaPublishedTypeResolver. - Pour chaque property type du schema, construit un
Func<object?>(valueFactory) selon l'editeur, enveloppe dansHeadlessPublishedProperty. - Renvoie un
HeadlessPublishedElement.
BuildValueFactoryFor(propertyType, token) dispatche par type d'editeur :
- RTE : extrait
markup->HtmlEncodedString. - MediaPicker3 : single ou array ->
MediaWithCrops(stubHeadlessPublishedMedia+ImageCropperValue, URL prefixee parDomainUrl, focal/crops) ; fallback URL brute. - BlockList : array ou
{ items }-> recursionCreateFromDeliverysur content/settings ->BlockListItem->BlockListModel. - BlockGrid : lit
gridColumns, reutiliseBlockGridItemConverterpour les areas imbriquees ->BlockGridModel. - Types simples : conversion typee (
bool,int,float,string).
HeadlessPublishedMedia (Factories/HeadlessPublishedMedia.cs) : stub minimal d'IPublishedContent pour les medias (Key, Name, ContentType).
4. Typage fort en vue : AsModel<T> (Extensions/PublishedElementExtensions.cs)
var content = Model.Content.AsModel<AccordionBlock>(PublishedValueFallback);- Si l'element est deja du bon type (contenu local), le retourne tel quel.
- Sinon, par reflexion, invoque le constructeur ModelsBuilder
ctor(IPublishedElement, IPublishedValueFallback)et renvoie le modele fort. Les getters du modele (.Title, ...) declenchent alors les valueFactory.
5. Flux JSON -> HTML
Delivery API (JSON) -> deserialisation avec converters -> HeadlessElementFactory reconstruit les IPublishedElement (valeurs paresseuses) -> les vues/partials appellent AsModel<T> puis les helpers Umbraco (Html.GetBlockGridHtmlAsync, rendu RTE par block) exactement comme pour du contenu local. Le RTE remplace les placeholders <umb-rte-block .../> par le HTML des blocks rendus.
Config & helpers
Configurations/HeadlessCmsApiOptions.cs:BaseUrl,ApiKey,DomainUrl(prefixe des URLs media),ArticleDetailCacheSeconds,WebhookSecret.Helpers/HeadlessJsonPaths.cs: alias de content type (accmArticle, icogneArticle, linfoArticle, genericArticle ; + dossiers) et chemins JSON (properties.title,properties.datePublished,properties.teaser.markup, auteurs, ...).- Invalidation de cache : le headless POST un webhook (
WebhookSecret) au changement d'article.
Caracteristiques cles
Evaluation paresseuse des valeurs ; dispatch par type d'editeur ; recursion pour les blocks imbriques ; reutilisation du converter au niveau article ET dans la factory ; fallback (URL media brute, commentaire HTML si partial manquant) ; mode preview (bypass cache + header).

