使用委托包装异常处理时,Web-API 调用不起作用

Posted

技术标签:

【中文标题】使用委托包装异常处理时,Web-API 调用不起作用【英文标题】:Web-API call not working when using delegate to wrap exception handling 【发布时间】:2019-09-28 03:34:45 【问题描述】:

我有一个带有自托管 OWIN 的 .NET 4.5.2 应用程序来支持内部 Web api。为了进行一般的异常处理,我尝试将所有调用包装在一个方法“TryOk”中,并以委托作为参数。 TryOk 然后会关心异常并处理它们。

我尝试在没有委托的情况下调用 web-api 并且它有效。只有在使用委托时我才会收到错误。

我将代码减少到最大,并像异步一样删除了所有代码镇流器。

[HttpPost, Route("echo")]
public IHttpActionResult MyEchoApi([FromBody]string echo)

    // this is working: direct return
    //return Ok("you say " + echo ?? "nothing");

    // this is also working: direct return with exception handling
    //try  return Ok(call()); 
    //catch (Exception ex)  return BadRequest(ex.Message); 

    // this is not working: wrapping in delegate
    return TryOk(() =>  return Ok("you say " + echo ?? "nothing"); );


private IHttpActionResult TryOk<T>(Func<T> call)

    try  return Ok(call()); 
    catch (Exception ex)  return BadRequest(ex.Message); 

我收到异常“从 'Microsoft.Owin.Host.HttpListener.RequestProcessing.HttpListenerStreamWrapper' 上的 'Length' 获取值时出错。”内部异常“此流不支持查找操作”。

异常详情:

"ExceptionMessage": "Error getting value from 'Length' on 'Microsoft.Owin.Host.HttpListener.RequestProcessing.HttpListenerStreamWrapper'.",
"ExceptionType": "Newtonsoft.Json.JsonSerializationException",
"StackTrace": "   bei Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)
   bei Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer, Object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, JsonContract& memberContract, Object& memberValue)
   bei Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   bei Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeDictionary(JsonWriter writer, IDictionary values, JsonDictionaryContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   bei Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   bei Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   bei Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeDictionary(JsonWriter writer, IDictionary values, JsonDictionaryContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   bei Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   bei Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   bei Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)
   bei Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)
   bei System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)
   bei System.Net.Http.Formatting.JsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)
   bei System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)
   --- Ende der Stapelüberwachung vom vorhergehenden Ort, an dem die Ausnahme ausgelöst wurde ---
   bei System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   bei System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   bei System.Web.Http.Owin.HttpMessageHandlerAdapter.<BufferResponseContentAsync>d__27.MoveNext()",
"InnerException": 
    "Message": "An error has occurred.",
    "ExceptionMessage": "This stream does not support seek operations.",
    "ExceptionType": "System.NotSupportedException",
    "StackTrace": "   bei System.Net.HttpResponseStream.get_Length()
    bei GetLength(Object )
    bei Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target)"

我做错了什么?

感谢您的任何回答或想法。

-- 爵士

【问题讨论】:

【参考方案1】:

您是creating a response over a response,这就是导致您出现该错误的原因。

在这一行,您在回调函数中返回了一个 IHttpActionResult:

 return TryOk(() =>  return Ok("you say " + echo ?? "nothing"); );

您无需拨打Ok(call()),只需拨打return call()即可。

private IHttpActionResult TryOk<T>(Func<T> call)

    // call itself returns an Ok http action result, you are returning a reposnse on another response! just change to call();
    try  return Ok(call()); 
    catch (Exception ex)  return BadRequest(ex.Message); 

编辑 1:

您甚至可以通过检查您的 callbac func 的返回值来使您的 TryOk 方法更加智能。

try

    var returnValue = call();

    if(returnValue is IHttpActionResult)
        return returnValue;

    return Ok(returnValue);

catch(Exception ex)

    return BadRequest(ex.Message);

我的另一个建议,我建议你使用全局Exception Filter。

【讨论】:

天哪,我犯了多么愚蠢的错误!我应该自己发现的。感谢您的快速答复。感谢您对前过滤器的推荐。 -jaz @jaz 错误总会发生,所以不要担心。

以上是关于使用委托包装异常处理时,Web-API 调用不起作用的主要内容,如果未能解决你的问题,请参考以下文章

AsyncSocket 不调用委托

如何将身份从 Web 应用程序委托给 WebAPI

2018第17周总结

PromiseKit 4 - 包装委托

观察 UIViewPropertyAnimator 正在运行问题

参数合法性校验