Expanding Nested Block for Content Delivery API v3 (CMS 12)

  • Updated

Many customers asking if there is a way to Expand nested blocks in CMS and we already have docs for this:
https://docs.developers.optimizely.com/content-management-system/v1.5.0-content-delivery-api/docs/how-to-customize-data-returned-to-clients

But sometimes they need a working example more than docs so below is a sample code that expands the Content Area that may help to fulfill the requirement:

 

PLEASE NOTED: the expanded level should be limited as it may impact the performance if it is too high.

I suggest to set them at 3 and maximum at 5

using System;
using EPiServer.ContentApi.Core.Serialization;
using EPiServer.ContentApi.Core.Serialization.Models;
using Microsoft.AspNetCore.Http;

namespace Foundation.Features.Api
{
    public class ExpandContentApiModelFilter : IContentApiModelFilter
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        private const string ExpandingScopeKey = "ContentDeliveryApi:ExpandingScope";
        private const int MaxExpandLevel = 3;

        public ExpandContentApiModelFilter(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }

        public Type HandledContentApiModel => typeof(ContentApiModel);

        public void Filter(ContentApiModel contentApiModel, ConverterContext converterContext)
        {
            foreach (var property in contentApiModel.Properties.Values)
            {
                if (property is ContentAreaPropertyModel contentArea)
                {
                    using (new ExpandingScope(_httpContextAccessor.HttpContext))
                    {
                        if (GetExpandingLevel(_httpContextAccessor.HttpContext) > MaxExpandLevel)
                        {
                            return;
                        }

                        if (contentArea.ExpandedValue is null)
                        {
                            contentArea.Expand(converterContext.Language);
                        }
                    }
                }
            }
        }

        private static int GetExpandingLevel(HttpContext context)
        {
            if (context.Items.TryGetValue(ExpandingScopeKey, out var value) && value is ExpandingContext expandingContext)
            {
                return expandingContext.Level;
            }

            return 0;
        }

        private class ExpandingContext
        {
            public int Level { get; set; } = 0;
        }

        private class ExpandingScope : IDisposable
        {
            private readonly HttpContext _requestContext;

            public ExpandingScope(HttpContext context)
            {
                _requestContext = context;
                if (context.Items.TryGetValue(ExpandingScopeKey, out var value) && value is ExpandingContext expandingContext)
                {
                    expandingContext.Level++;
                }
                else
                {
                    context.Items[ExpandingScopeKey] = new ExpandingContext();
                }
            }

            public void Dispose()
            {
                if (_requestContext.Items[ExpandingScopeKey] is ExpandingContext currentContext)
                {
                    if (currentContext.Level > 0)
                    {
                        currentContext.Level--;
                    }
                }
                else
                {
                    throw new InvalidOperationException();
                }
            }
        }
    }
}

After that, we will need to add these codes to the startup.cs file

services.TryAddEnumerable(ServiceDescriptor.Singleton<IContentApiModelFilter, ExpandContentApiModelFilter>());

Run your site and from now the request will have the expanded value:

nested_after.PNG