前言
看了下推送記錄,一個月前,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
,但需要置頂Include
的then
引數,配置套娃查詢……
繼續!
那有沒有什麼辦法是不改動介面程式碼的情況下,解決介面套娃的問題?
答案肯定有啦
這就要用NewtonsoftJson
了~
首先安裝Microsoft.AspNetCore.Mvc.NewtonsoftJson
這個nuget包
然後在服務配置裡面新增程式碼
services.AddControllersWithViews()
.AddNewtonsoftJson(options => {
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
然後再請求介面,返回的結果就跟上面的一樣啦~
會導致套娃遞迴的屬性直接變成null~
PS:這個程式碼的作用就是把WebApi預設的json序列化器從System.Text.Json
改成NewtonsoftJson
,並且配置處理套娃遞迴的方式為忽略~