首页 > 解决方案 > ASP.NET API 向 Angular 返回奇怪的值

问题描述

我的 ASP.NET API 与 Angular 对话时似乎遇到了一些问题,我不完全确定原因。我希望有人可以看看我所做的事情并寻找任何明显的东西。

在我的 API 控制器中,我有以下代码:

// Data Structure to hold results from query
public class MyContainer
    {
        public ulong id;
        public int num;

        public MyContainer(ulong id, int num)
        {
            this.id = id;
            this.num = num;
        }
    }


public class MyAPIController : ControllerBase {

        [Route("/Testing")]
        [AllowAnonymous]
        [HttpGet]
        public IEnumerable<MyContainer> GetMethod()
        {
            var x = from r in context.TableOne
                    group r by r.Id into g
                    select new MyContainer(g.Key, g.Count() );

            var y = x.First();
            logger.LogInformation(y.num.ToString());  // This outputs correctly!

            return x.ToList();
        }
}

在 Angular 中,我有以下服务:

const httpOptions = {
    headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};

class MyContainer {
    public id: number;
    public num: number;
}

export class MyService {
     ...
     getMethod(): Observable<MyContainer> {
        let url = this.baseUrl + 'Testing';
        let test = this.http.get(url, httpOptions);

        // This outputs an array with the correct number of elements, but they are all blank...
        // Such as [{…}, {…}, {…}], and when expanded, they are empty.
        test.subscribe(x => console.log(x));
        
        return test;
    }
}

请注意,我在上面的代码中添加了三个注释以帮助突出我的问题。

我刚刚开始学习 ASP.NET,所以我想我错过了一些明显的东西。

我所知道的有效:

  1. 路由工作——我可以毫无问题地使用我的 API 的 GetMethod()。
  2. 我的 LINQ 数据库查询——正如第二条评论中提到的,从 API 控制器中的记录器中,我看到我的 API 函数成功地查询了数据库并返回了我期望的值。它只是没有正确地传递给 Angular。

我试过的:

  1. [ApiController]属性添加到我的 API 控制器。
    • 这实际上给了我一个奇怪的 400 错误,“输入不包含不包含任何 JSON 令牌......”?
  2. 使用 POST 而不是 GET。
  3. 修改返回中使用的数据结构(即尝试List<MyContainer>
  4. 使用原始 SQL 而不是 LINQ。
  5. 更改MyContainer为仅使用字符串 + 将数据库查询中的结果转换为字符串(以查看是否可能是序列化问题)。

我认为问题可能是:

  1. ASP.NET/Angular 不喜欢我通过我的MyContainer班级返回数据。
  2. 由于我使用的是 Entity Core 框架,它可能不喜欢MyContainer没有注册为“无键实体类型”,但它没有给我任何错误,所以我认为这不是问题。
  3. 每次我去测试这个 API 函数时,上帝都会以正确的方式发射宇宙射线来撞击将 API 返回结果存储在 Angular 内存中的电子,从而给我正确数量的空白元素。

非常感谢任何帮助!我觉得这与我使用自定义数据类型有关,但是我也不知道为什么这会是一个问题,除非它无法转换为 JSON ......

编辑:使用以下库

using System;
using System.Data.SqlClient;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

标签: c#asp.netangular

解决方案


问题是您的 DTO ( MyContainer) 没有序列化其字段。IIRC,默认情况下,.NET 的大多数 JSON 序列化程序只序列化公共属性,而不是公共字段。

我假设您正在使用Newtonsoft.Json,但将其转换为System.Text.Json

主要修复是更改您的MyContainerDTO 以使用属性而不是字段。请注意 usingJsonProperty是可选的,但我更喜欢设置显式名称(并且不要camelCase用于 .NET 中的公共实例成员,仅使用TitleCase):

public class MyContainer
{
    public MyContainer(ulong id, int number)
    {
        this.Id     = id;
        this.Number = num;
    }

    [JsonProperty( "id" )]
    public UInt64 Id { get; }

    [JsonProperty( "num" )]
    public Int32 Number { get; }
}

其次,我会像这样改进您的控制器操作,但请阅读这些文章:

[ApiController]
public class MyApiController : ControllerBase
{
    [Route("/Testing")] // <-- This implicitly allows GET and HEAD, you don't need a separate [HttpGet] attribute.
    [AllowAnonymous]
    [ProducesResponseType(StatusCodes.Status200OK, typeof(List<MyContainer>))]
    public async Task<IActionResult> GetMethod()
    {
        // Do data access asynchronously...
        var groups = await this.context
            .TableOne
            .GroupBy( r => r.Id )
            .Select( grp => new { Id = grp.Key, Count = grp.Count() } )
            .ToListAsync();
            
        // ...but transform the data synchronously:
        List<MyContainer> list = groups
            .Select( g => new MyContainer( g.Id, g.Count ) )
            .ToList();

        return this.OK( list );
    }
}

在您的 TypeScript 代码中,使用只读接口而不是类来表示 DTO 通常是一个更好的主意,因为您可以将反序列化的 JSON(以及一般的对象文字)有意义地转换为接口,但不能将对象文字有意义地转换为类(如JSON对象无论如何都没有函数属性或继承):

所以改变这个:

class MyContainer {
    public id: number;
    public num: number;
}

对此:

interface MyContainer {
    readonly id : number;
    readonly num: number;
}

推荐阅读