Asp-Net-Core開發筆記:介面返回json物件出現套娃遞迴問題

程式設計實驗室發表於2022-01-26

前言

看了下推送記錄,一個月前,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,並且配置處理套娃遞迴的方式為忽略~

參考文件

相關文章