[英]How to handle errors in method that has IAsyncEnumerable as return type
I have an API endpoint:我有一个 API 端点:
[HttpGet("api/query")]
public async IAsyncEnumerable<dynamic> Query(string name)
{
await foreach(var item in _myService.CallSomethingReturningAsyncStream(name))
{
yield return item;
}
}
I would like to be able to in case of an ArgumentException return something like "Bad request" response.我希望能够在 ArgumentException 的情况下返回类似“错误请求”的响应。
If I try using try-catch block, I get error :如果我尝试使用 try-catch 块,我会收到错误消息:
CS1626: Cannot yield a value in the body of a try block with a catch clause
CS1626:无法在带有 catch 子句的 try 块主体中产生值
Please note that it is an API endpoint method, so error handling should ideally be in the same method, without need for making additional middlewares.请注意,它是一个 API 端点方法,因此理想情况下错误处理应该在同一个方法中,而不需要制作额外的中间件。
You have to split method.你必须拆分方法。 Extract part which does async processing:
提取进行异步处理的部分:
private async IAsyncEnumerable<dynamic> ProcessData(TypeOfYourData data)
{
await foreach(var item in data)
{
yield return item;
}
}
And then in API method do:然后在 API 方法中执行:
[HttpGet("api/query")]
public IActionResult Query(string name)
{
TypeOfYourData data;
try {
data = _myService.CallSomethingReturningAsyncStream(name);
}
catch (...) {
// do what you need
return BadRequest();
}
return Ok(ProcessData(data));
}
Or actually you can just move the whole thing into separate method:或者实际上你可以将整个事情移动到单独的方法中:
[HttpGet("api/query")]
public IActionResult Query(string name)
{
try {
return Ok(TheMethodYouMovedYourCurrentCodeTo);
}
catch (...) {
// do what you need
return BadRequest();
}
}
It will of course only catch exceptions thrown before actual async enumeration starts, but that's fine for your use case as I understand.它当然只会捕获在实际异步枚举开始之前抛出的异常,但据我了解,这对您的用例来说很好。 Returning bad request after async enumeration has started is not possible, because response is already being sent to client.
在异步枚举开始后返回错误的请求是不可能的,因为响应已经被发送到客户端。
You could install the System.Interactive.Async package, and do this:您可以安装System.Interactive.Async package,然后执行以下操作:
[HttpGet("api/query")]
public IAsyncEnumerable<dynamic> Query(string name)
{
return AsyncEnumerableEx.Defer(() => _myService.CallSomethingReturningAsyncStream(name))
.Catch<dynamic, Exception>(ex =>
AsyncEnumerableEx.Return<dynamic>($"Bad request: {ex.Message}"));
}
The signature of the Defer
operator: Defer
算子的签名:
// Returns an async-enumerable sequence that invokes the specified
// factory function whenever a new observer subscribes.
public static IAsyncEnumerable<TSource> Defer<TSource>(
Func<IAsyncEnumerable<TSource>> factory)
The signature of the Catch
operator: Catch
算子的签名:
// Continues an async-enumerable sequence that is terminated by
// an exception of the specified type with the async-enumerable sequence
// produced by the handler.
public static IAsyncEnumerable<TSource> Catch<TSource, TException>(
this IAsyncEnumerable<TSource> source,
Func<TException, IAsyncEnumerable<TSource>> handler);
The signature of the Return
operator: Return
操作员的签名:
// Returns an async-enumerable sequence that contains a single element.
public static IAsyncEnumerable<TValue> Return<TValue>(TValue value)
The Defer
might seem superficial, but it is needed for the case that the _myService.CallSomethingReturningAsyncStream
throws synchronously. Defer
可能看起来很肤浅,但在_myService.CallSomethingReturningAsyncStream
同步抛出的情况下需要它。 In case this method is implemented as an async
iterator , it will never throw synchronously, so you could omit the Defer
.如果此方法作为
async
迭代器实现,它永远不会同步抛出异常,因此您可以省略Defer
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.