Asp-Net-Core Development Notes: Nested Recursion Problem When API Returns JSON Objects
Preface I checked the posting history, a month ago, OK, I've become a monthly updater again o(╯□╰)o, this is absolutely not OK! [○・`Д´・ ○] So I'm posting an update today It's not that I'm too lazy to update or too busy; actually I've been writing a very long blog post and haven't finished it yet ( Ĭ ^ Ĭ ) Alright, let's get to the point... There's a WebApi serialization issue related to design, but it often comes up when dealing with associated fields. Entity Classes Let's first look at the entity class definitions. Due to space constraints, only a few key fields are kept. public class
目录7 个章节
Preface
I checked the posting history, a month ago, OK, I've become a monthly updater again o(╯□╰)o, this is absolutely not OK! [○・`Д´・ ○]
So I'm posting an update today
It's not that I'm too lazy to update or too busy; actually I've been writing a very long blog post and haven't finished it yet ( Ĭ ^ Ĭ )
Alright, let's get to the point...
There's a WebApi serialization issue related to design, but it often comes up when dealing with associated fields.
Entity Classes
Let's first look at the entity class definitions. Due to space constraints, only a few key fields are kept.
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; }
}
User entity class:
public class User : EntityBase {
/// <summary>
/// 用户名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 用户创建的爬虫
/// </summary>
public List<CrawlTask> CrawlTasks { get; set; }
}
API
Then the API is written like this:
/// <summary>
/// 获取用户创建的全部爬虫
/// </summary>
/// <returns></returns>
[HttpGet]
public ActionResult<List<CrawlTask>> GetAll() {
var user = _authService.GetUser(User.Identity?.Name);
return user.CrawlTasks;
}
Then when we request this API, the data we expect is:
[
{
"name": "爬虫名称",
"user": {
"name": "用户名"
},
"userId": "0f3d4b2f-3b4e-4d08-8f4c-0009a316f041",
"id": "4d52d83b-f3ec-47c6-ab26-e241c09c14d1"
}
]
Error
But in reality it throws an error directly:
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.
Obviously, the returned object has nested recursion.
Notice that Path:$.User.CrawlTasks.User.CrawlTasks.User.Crawl... , the JSON data we expected above is:
{
"name": "test crawl123",
"user": {
"name": "string"
},
"userId": "0f3d4b2f-3b4e-4d08-8f4c-0009a316f041",
"id": "4d52d83b-f3ec-47c6-ab26-e241c09c14d1"
}
That is, the User under the Crawl object should only have the Name property, and the CrawlTasks list should not be displayed as well. But the program doesn't know that — User has CrawlTasks, and then CrawlTasks contains User again, which falls into nested recursion...
Initial Solution
Clearly, this has issues with the design and the way data is retrieved. It can be avoided by changing the query approach, for example:
[HttpGet]
public ActionResult<List<CrawlTask>> GetAll() {
return _crawlRepo
.Where(a => a.UserId == User.Identity.Name)
.ToList();
}
Because the navigation property User of Crawl is not requested here, the User object information will not be read, and the result looks like this:
[
{
"name": "test crawl123",
"user": null,
"userId": "0f3d4b2f-3b4e-4d08-8f4c-0009a316f041",
"id": "4d52d83b-f3ec-47c6-ab26-e241c09c14d1"
}
]
As you can see, the value of the User object is null, which is already sufficient for the API. After all, this is getting all crawlers of the current user, so the user property of all crawlers is the same — no need to repeat it.
However, even adding the User object is completely fine. Let's modify the API here and see the effect:
[HttpGet]
public ActionResult<List<CrawlTask>> GetAll() {
return _crawlRepo.Select
.Where(a => a.UserId == User.Identity.Name)
.Include(a => a.User) // 添加了这行代码,请求关联对象
.ToList();
}
The returned result:
[
{
"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"
}
]
As you can see, in the returned Crawl object, the crawlTasks property inside the User object is empty, because of that line of code we added earlier: .Include(a => a.User). FreeSQL also supports further querying the navigation property crawlTasks of User, but you need to specify the then parameter of Include to configure nested queries...
Let's Continue!
Is there a way to solve the API nested recursion problem without changing the API code?
Of course there is
This is where we need NewtonsoftJson~
First, install the Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet package
Then add the following code in the service configuration
services.AddControllersWithViews()
.AddNewtonsoftJson(options => {
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
Then request the API again, and the returned result will be the same as above~
Properties that would cause nested recursion will directly become null~
PS: The purpose of this code is to change the WebApi default JSON serializer from System.Text.Json to NewtonsoftJson, and configure the way nested recursion is handled to ignore~
References
_EOF_
Asp-Net-Core Development Notes: Nested Recursion Problem When API Returns JSON Objects
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
Asp-Net-Core开发笔记:EFCore统一实体和属性命名风格
EFCore居然没有内置命名转换?得自己写正则或用插件,真是折腾。插件一行代码搞定,但为什么EFCore不直接支持?命名风格转换,要么自己写代码,要么依赖第三方,真费劲。
2023-10-10 · Asp-Net-Core开发笔记 · 约 1 分钟
Asp-Net-Core开发笔记:实现动态审计日志功能
审计日志不仅仅是一种遵循最佳实践的手段,更是确保数据安全、提高系统透明度、促进责任归属明晰的关键工具。
2024-03-21 · Asp-Net-Core开发笔记 · 约 1 分钟
Asp-Net-Core开发笔记:给SwaggerUI加上登录保护功能
既要在项目发布后访问 SwaggerUI ,又要保证一定的安全性,本文提供的思路或许是一种比较简单又有效的解决方案。
2024-05-21 · Asp-Net-Core开发笔记 · 约 1 分钟
Asp-Net-Core开发笔记:使用alpine镜像并加入健康检查
用alpine镜像?不过是把体积砍半,健康检查加个路径就完事。wget自带,何必装curl,真是多余。发布选linux-musl-x64,不然别想跑。
2024-04-19 · Asp-Net-Core开发笔记 · 约 1 分钟
讨论
共 0 条加载评论中…