0

我有一个返回 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....
    }
}

更新

一旦结果可用,我希望此流式传输到客户端。如果我返回Task<IEnumerable<string>>,那么整个列表/数组/集合必须在移交给System.Text.Json进行序列化之前完成。我不认为这是非常节省内存的。

4

1 回答 1

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&IAsyncEnumerable生成器方法在第一次调用.MoveNext[Async].

因为您之前的实现会IAsyncEnumerable在执行任何操作之前返回到 MVC。在调用您的服务IAsyncEnumerable.MoveNextAsync之前,将编写 HTTP 标头并调用 json 序列化程序。GetStringAsync

如果您想在返回 200 / 404 之前等待第一个结果,那么您必须等待后端服务返回某些内容,然后才能编写任何 HTTP 标头。

在这里使用第一个字节的时间作为度量是完全误导的。

于 2021-09-27T08:15:17.783 回答