I have an API endpoint:
[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.
If I try using try-catch block, I get error :
CS1626: Cannot yield a value in the body of a try block with a catch clause
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.
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:
[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:
[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:
// 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:
// 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:
// 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. In case this method is implemented as an async
iterator , it will never throw synchronously, so you could omit the Defer
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.