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