ASP. NET API возвращает странные значения в Angular - PullRequest
0 голосов
/ 06 августа 2020

Похоже, у меня есть проблемы с моим 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. Использование raw SQL вместо LINQ.
  5. Изменение MyContainer использовать только строки + преобразование результатов запроса к базе данных в строки (чтобы увидеть, может ли это быть проблемой сериализации).

Что, по моему мнению, могут быть проблемы:

  1. ASP .NET / Angular мне не нравится, что я возвращаю данные через мой MyContainer класс.
  2. Поскольку я использую платформу Entity Core, это может не понравиться этот MyContainer не зарегистрирован как «Тип объекта без ключа», но он не дает мне никаких ошибок, поэтому я не думаю, что это проблема.
  3. Каждый раз, когда я go проверяю это Функция API, Бог посылает космические c лучи правильным образом, чтобы поразить электроны, сохраняя результат возврата 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;

1 Ответ

3 голосов
/ 06 августа 2020

Проблема в том, что ваш DTO (MyContainer) не сериализует свои поля. IIR C, по умолчанию, большинство сериализаторов JSON для. NET сериализуют только свойства publi c, а не поля publi c.

Я предполагаю, что вы используете Newtonsoft.Json, но преобразовать это в System.Text.Json:

тривиально. Основное исправление - изменить MyContainer DTO, чтобы использовать свойства вместо полей. Обратите внимание, что использование JsonProperty необязательно, но я предпочитаю указывать явные имена (и не использовать camelCase для членов экземпляра publi c в. 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...