返回文章列表文章索引阅读全文
STARBLOG / 阅读手记
Asp-Net-Core开发笔记

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

曦远 (DealiAxy)约 15 分钟
目录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 许可协议。转载请注明出处!

相关文章

讨论

共 0 条

良言一句三冬暖,恶语伤人六月寒。

加载评论中…