• AspNetCore开发笔记:接口返回json对象出现套娃递归问题


    前言

    看了下推送记录,一个月前,OK,我又变成月更了o(╯□╰)o,这绝对不行![○・`Д´・ ○]

    所以今天来更新了

    其实不是我懒得更新或者是太忙,其实是最近在写一篇很长的博客,一直没写完( Ĭ ^ Ĭ )

    好吧,先进入正题……

    有一个关于WebApi序列化的问题,跟设计有关,但在涉及到关联字段的时候经常会遇到。

    实体类

    先看看实体类定义,限于篇幅,只保留几个关键字段。

    public class CrawlTask : EntityBase {
        /// <summary>
        /// 爬虫名称
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 创建这个爬虫的用户
        /// </summary>
        public User User { get; set; }
        /// <summary>
        /// 用户ID
        /// </summary>
        public string? UserId { get; set; }
    }
    

    用户实体类:

    public class User : EntityBase {
        /// <summary>
        /// 用户名
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 用户创建的爬虫
        /// </summary>
        public List<CrawlTask> CrawlTasks { get; set; }
    }
    

    接口

    然后接口这样写:

    /// <summary>
    /// 获取用户创建的全部爬虫
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    public ActionResult<List<CrawlTask>> GetAll() {
        var user = _authService.GetUser(User.Identity?.Name);
        return user.CrawlTasks;
    }
    

    然后请求这个接口,我们期望的数据是:

    [
      {
        "name": "爬虫名称",
        "user": {
        	"name": "用户名"
        },
        "userId": "0f3d4b2f-3b4e-4d08-8f4c-0009a316f041",
        "id": "4d52d83b-f3ec-47c6-ab26-e241c09c14d1"
      }
    ]
    

    报错

    但事实是直接报错:

    System.Text.Json.JsonException: A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32. Consider using ReferenceHandler.Preserve on JsonSerializerOptions to support cycles.
    Path: $.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.User.CrawlTasks.Name.
    

    很明显,返回的对象套娃递归了。

    注意那个Path:$.User.CrawlTasks.User.CrawlTasks.User.Crawl... ,我们上面期望的json数据是:

    {
        "name": "test crawl123",
        "user": {
            "name": "string"
        },
        "userId": "0f3d4b2f-3b4e-4d08-8f4c-0009a316f041",
        "id": "4d52d83b-f3ec-47c6-ab26-e241c09c14d1"
    }
    

    Crawl对象下的User只有Name属性,不要把CrawlTasks列表也显示出来,但程序它不知道啊,User里有CrawlTasks,然后CrawlTasks里面又有User,这就陷入一个套娃递归了……

    初步解决

    很明显,这根设计和数据获取方式有问题,可以通过换个查询方式来避免,比如:

    [HttpGet]
    public ActionResult<List<CrawlTask>> GetAll() {
        return _crawlRepo
            .Where(a => a.UserId == User.Identity.Name)
            .ToList();
    }
    

    因为这里没有请求Crawl的导航属性User,所以不会读取User对象的信息,出现的结果是这样:

    [
      {
        "name": "test crawl123",
        "user": null,
        "userId": "0f3d4b2f-3b4e-4d08-8f4c-0009a316f041",
        "id": "4d52d83b-f3ec-47c6-ab26-e241c09c14d1"
      }
    ]
    

    可以看到User对象的值是null,对于接口来说已经够用了,毕竟这是获取当前用户的所有爬虫,所有爬虫的user属性都是同一个,没必要重复啦。

    不过即使把User对象加上也是完全没问题的,这里改一下接口看一下效果:

    [HttpGet]
    public ActionResult<List<CrawlTask>> GetAll() {
        return _crawlRepo.Select
            .Where(a => a.UserId == User.Identity.Name)
            .Include(a => a.User)		// 添加了这行代码,请求关联对象
            .ToList();
    }
    

    返回的结果:

    [
      {
        "name": "test crawl123",
        "user": {
          "name": "string",
          "crawlTasks": null,
          "id": "0f3d4b2f-3b4e-4d08-8f4c-0009a316f041"
        },
        "userId": "0f3d4b2f-3b4e-4d08-8f4c-0009a316f041",
        "id": "4d52d83b-f3ec-47c6-ab26-e241c09c14d1"
      }
    ]
    

    可以看到,返回的Crawl对象中,User对象里的crawlTasks属性是空的,因为我们前面加的那行代码:.Include(a => a.User),FreeSQL还支持进一步查询User的导航属性crawlTasks,但需要置顶Includethen参数,配置套娃查询……

    继续!

    那有没有什么办法是不改动接口代码的情况下,解决接口套娃的问题?

    答案肯定有啦

    这就要用NewtonsoftJson了~

    首先安装Microsoft.AspNetCore.Mvc.NewtonsoftJson这个nuget包

    然后在服务配置里面添加代码

    services.AddControllersWithViews()
        .AddNewtonsoftJson(options => {
            options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
        });
    

    然后再请求接口,返回的结果就跟上面的一样啦~

    会导致套娃递归的属性直接变成null~

    PS:这个代码的作用就是把WebApi默认的json序列化器从System.Text.Json改成NewtonsoftJson,并且配置处理套娃递归的方式为忽略~

    参考文档

  • 相关阅读:
    ZooKeeper的工作原理
    redis 数据类型详解 以及 redis适用场景场合
    nginx负载均衡原理
    Java中缓存的介绍
    Java中接口的作用
    json与xml的区别
    最经典40个多线程问题总结
    Java线程 : 线程同步与锁
    dbcp与c3p0的区别
    Linux常见命令
  • 原文地址:https://www.cnblogs.com/deali/p/15847475.html
Copyright © 2020-2023  润新知