Как установить код состояния 404 при возврате пустого IAsyncEnumerable <T> в ASP.NET Core (.NET 6)

У меня есть действие контроллера, которое возвращает IAsyncEnumerable. Он запрашивает источник данных, и, если данные доступны, каждый результат представляет собой «yield return» -ed (сериализуется с помощью System.Text.Json и возвращается клиенту). Но как я могу установить код состояния на 404, если результатов нет? Прямо сейчас, если результатов нет, метод возвращает пустой массив JSON с кодом состояния = 200.

      public async IAsyncEnumerable<string> GetStrings()
{
    IEnumerable<string> stringResults = await myData.GetStringsAsync();

    if (stringResults is object && stringResults.Any())
    {
        foreach (string result in stringResults)
        {
            yield return result;
        }
    }
    else
    {
        return NotFound(); //CS1622: Cannot return a value from an iterator....
    }
}

Обновлять

Я хочу, чтобы эта потоковая передача была предоставлена ​​клиенту, как только будут получены результаты. Если я вернусь , то весь список / массив / коллекция должен быть завершен перед передачей в System.Text.Json для сериализации. Я не думаю, что это очень эффективно с точки зрения памяти.

1 ответ

Вы не можете смешивать асинхронный генератор и действие mvc, которое возвращает NotFound(). Но можно написать двумя способами.

      public async Task<IActionResult> GetStrings()
{
    IEnumerable<string> stringResults = await myData.GetStringsAsync();
    if (stringResults == null)
        return NotFound();
    var e = stringResults.GetEnumerator();
    if (!e.MoveNext())
        return NotFound();
    return Ok(AsEnum(e));

    async IAsyncEnumerable<string> AsEnum(IEnumerator<string> e){
        do {
            yield return e.Current;
        } while (e.MoveNext());
    }
}

С любой дополнительной обработкой ошибок и удалением счетчика.

Однако здесь все же есть разница в поведении. В отличие от async Task метод IEnumerable & методы генератора не запускаются до первого вызова .MoveNext[Async].

Поскольку ваша предыдущая реализация вернула бы IAsyncEnumerableв MVC перед выполнением любого вашего действия. Заголовки HTTP будут написаны, и сериализатор json вызовет IAsyncEnumerable.MoveNextAsync, перед твоим GetStringAsync сервис даже называется.

Если вы хотите дождаться первого результата, прежде чем возвращать 200/404, вам нужно дождаться, пока серверная служба что-то вернет, прежде чем вы сможете писать какие-либо заголовки HTTP.

Использование времени до первого байта в качестве метрики здесь полностью вводит в заблуждение.

Другие вопросы по тегам