c# - 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,所以我想我错过了一些明显的东西。
我所知道的有效:
- 路由工作——我可以毫无问题地使用我的 API 的 GetMethod()。
- 我的 LINQ 数据库查询——正如第二条评论中提到的,从 API 控制器中的记录器中,我看到我的 API 函数成功地查询了数据库并返回了我期望的值。它只是没有正确地传递给 Angular。
我试过的:
- 将
[ApiController]
属性添加到我的 API 控制器。- 这实际上给了我一个奇怪的 400 错误,“输入不包含不包含任何 JSON 令牌......”?
- 使用 POST 而不是 GET。
- 修改返回中使用的数据结构(即尝试
List<MyContainer>
) - 使用原始 SQL 而不是 LINQ。
- 更改
MyContainer
为仅使用字符串 + 将数据库查询中的结果转换为字符串(以查看是否可能是序列化问题)。
我认为问题可能是:
- ASP.NET/Angular 不喜欢我通过我的
MyContainer
班级返回数据。 - 由于我使用的是 Entity Core 框架,它可能不喜欢
MyContainer
没有注册为“无键实体类型”,但它没有给我任何错误,所以我认为这不是问题。 - 每次我去测试这个 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;
解决方案
问题是您的 DTO ( MyContainer
) 没有序列化其字段。IIRC,默认情况下,.NET 的大多数 JSON 序列化程序只序列化公共属性,而不是公共字段。
我假设您正在使用Newtonsoft.Json
,但将其转换为System.Text.Json
:
主要修复是更改您的MyContainer
DTO 以使用属性而不是字段。请注意 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; }
}
其次,我会像这样改进您的控制器操作,但请阅读这些文章:
- https://docs.microsoft.com/en-us/aspnet/core/web-api/advanced/conventions?view=aspnetcore-3.1
- https://docs.microsoft.com/en-us/aspnet/core/web-api/action-return-types?view=aspnetcore-3.1
[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;
}
推荐阅读
- javascript - 如何在字段集中显示 lat 和 lng 坐标()?
- python - 从现在到下一个小时的第 0 分钟的时间
- google-apps-script - 如何使 gscript gmail 插件演示运行?
- symfony - 在 Symfony 5 中清除缓存
- javascript - 如何找到与特定模式匹配的所有标签?
- objective-c - 如何将回调对象从 Wwift 传递到 Objective C 类并用作回调?
- c++ - 特殊的病毒函数修复了未解析的外部符号
- android - 第 3 页初始加载未显示
- apache-spark - n 个任务的序列化结果的总大小 (x MB) 大于 spark.driver.maxResultSize
- swift - 通过 iOS 应用触发谷歌云功能的最佳方式