You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Umbraco REST API的PublishedContentController处理嵌套内容时抛出异常

Umbraco PublishedContentController无法序列化NestedContent属性:问题原因与解决方案

我帮你拆解下这个问题的核心原因,再给你几个可行的解决办法:

问题根源

你遇到的这个序列化错误,本质是PublishedContentController和ContentController处理NestedContent的逻辑差异导致的:

  • ContentController返回的是数据库里的原始IContent对象,NestedContent属性是以JSON字符串的形式直接存储的,序列化时就是简单输出这个字符串,不会有循环引用的问题。
  • PublishedContentController返回的是经过Umbraco发布管道处理后的IPublishedContent对象,NestedContent会被自动解析成List<IPublishedContent>集合。而这些嵌套的内容实例内部可能包含对父内容的引用,或者自身结构存在循环依赖,Newtonsoft.Json默认配置没处理这种情况,所以抛出了Self referencing loop detected异常。

解决方案

这里提供三种可行的解决方法,你可以根据项目需求选择:

1. 全局配置JSON序列化忽略自引用循环

这是最简单的全局解决办法,修改JsonSerializerSettings让序列化器自动忽略循环引用:
在项目的Global.asax.cs中,更新Application_Start方法:

protected void Application_Start()
{
    // 保留原有Umbraco初始化代码
    AreaRegistration.RegisterAllAreas();
    
    // 配置JSON序列化处理循环引用
    GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    
    // 其他初始化代码
}

如果项目使用Owin启动(Startup.cs),则在Configuration方法中添加配置:

public void Configuration(IAppBuilder app)
{
    var config = new HttpConfiguration();
    // 配置循环引用处理
    config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    
    app.UseWebApi(config);
    // 其他Umbraco Owin配置代码
}

这个方法会全局生效,所有Web API的JSON序列化都会忽略循环引用,既能解决NestedContent的问题,也能避免其他潜在的循环引用错误。

2. 自定义PublishedContentController处理NestedContent

如果你不想全局修改序列化规则,可以创建自定义的PublishedContentController,手动把NestedContent属性转换成JSON字符串(和ContentController的行为一致):

using System.Collections.Generic;
using System.Web.Http;
using Umbraco.Web;
using Umbraco.Web.WebApi;

public class CustomPublishedContentController : PublishedContentController
{
    public override IHttpActionResult GetById(int id)
    {
        var publishedContent = Umbraco.TypedContent(id);
        if (publishedContent == null)
        {
            return NotFound();
        }

        // 构建自定义返回对象,手动处理属性
        var response = new Dictionary<string, object>
        {
            ["id"] = publishedContent.Id,
            ["name"] = publishedContent.Name,
            ["contentTypeAlias"] = publishedContent.ContentType.Alias,
            ["parentId"] = publishedContent.Parent?.Id ?? -1,
            ["createDate"] = publishedContent.CreateDate,
            ["updateDate"] = publishedContent.UpdateDate
        };

        var properties = new Dictionary<string, object>();
        foreach (var property in publishedContent.Properties)
        {
            // 判断是否是NestedContent属性
            if (property.PropertyType.PropertyEditorAlias == "Umbraco.NestedContent")
            {
                // 从原始内容中获取存储的JSON字符串
                var rawNestedValue = Umbraco.Content(publishedContent.Id).GetPropertyValue<string>(property.Alias);
                properties[property.Alias] = rawNestedValue;
            }
            else
            {
                // 其他属性直接使用发布后的值
                properties[property.Alias] = property.Value;
            }
        }

        response["properties"] = properties;
        return Ok(response);
    }

    // 可按需重写其他方法,比如 GetByRoute 等
}

之后需要通过Web API路由配置,让自定义控制器替换原有的PublishedContentController路由。

3. 使用DTO(数据传输对象)映射IPublishedContent

第三种方法是创建专门的DTO类,只映射你需要的字段,避免序列化整个IPublishedContent对象:

// 定义DTO类
public class PublishedContentDto
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string ContentTypeAlias { get; set; }
    public int ParentId { get; set; }
    public Dictionary<string, object> Properties { get; set; }
    // 按需添加其他字段
}

// 在自定义控制器中使用DTO
public class DtoPublishedContentController : UmbracoApiController
{
    public IHttpActionResult GetById(int id)
    {
        var content = Umbraco.TypedContent(id);
        if (content == null)
        {
            return NotFound();
        }

        var dto = new PublishedContentDto
        {
            Id = content.Id,
            Name = content.Name,
            ContentTypeAlias = content.ContentType.Alias,
            ParentId = content.Parent?.Id ?? -1,
            Properties = new Dictionary<string, object>()
        };

        foreach (var prop in content.Properties)
        {
            if (prop.PropertyType.PropertyEditorAlias == "Umbraco.NestedContent")
            {
                // 获取原始JSON字符串
                var rawValue = Umbraco.Content(content.Id).GetPropertyValue<string>(prop.Alias);
                dto.Properties[prop.Alias] = rawValue;
            }
            else
            {
                dto.Properties[prop.Alias] = prop.Value;
            }
        }

        return Ok(dto);
    }
}

这种方法能完全控制返回的数据结构,避免不必要的字段被序列化,彻底解决循环引用问题。

内容的提问来源于stack exchange,提问作者Igor Besedin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:32:49