处理类型化的Web API HttpClient调用中的错误

时间:2014-06-09 12:12:55

标签: asp.net-web-api error-handling async-await dotnet-httpclient

我对一个返回任务的webapi客户端进行了多次调用 像这样的东西

public async Task<TResp> GetMyThingAsync(TReq req)
{
    using (HttpClient client = new HttpClient())
    {
        client.BaseAddress = new Uri(BaseURI);
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
        client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/xml"));

        await HttpRuntime.Cache.GetToken().ContinueWith((t) =>
        {
            client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("XXX", t.Result);
        });

        var httpResponseMessage = await client.PostAsXmlAsync<TReq>("This/That/", req);
        httpResponseMessage.EnsureSuccessStatusCode();
        var resp = await httpResponseMessage.Content.ReadAsAsync<TResp>();
        return resp;
    }
}

对api的调用当然可以返回500或其他一些问题。如果类似的事情发生,EnsureSuccessStatusCode()显然会抛出,但是对于响应中的任何信息做任何事都为时已晚。 有一个很好的方法来处理这个? 我知道你可以在客户端添加一个messageHandler,比如 HttpClient client = HttpClientFactory(new ErrorMessageHandler()) ..

var customHandler = new ErrorMessageHandler()
        { InnerHandler = new HttpClientHandler()};
HttpClient client = new HttpClient(customHandler);

这是要走的路吗? ErroMessageHandler会是什么样子,并且会向调用控制器返回一些有用的东西......?

非常感谢

NAT

1 个答案:

答案 0 :(得分:1)

创建自定义处理程序可以成为记录异常或验证响应的优雅解决方案。如果遇到异常,我不确定被叫控制器是否正在等待来自客户端的有意义的响应。我认为真正重要的部分是确保您(客户端)优雅地处理Web apis错误。

这可以通过以下两种方式完成:

  1. 您可以在调用方法中本地处理异常。您可以使用HttpResponseMessage.IsSuccessStatusCode属性指示是否返回错误响应而不是调用引发异常的httpResponseMessage.EnsureSuccessStatusCode(),并返回自定义ErrorResponse(或执行您决定的任何操作):

    var client = new HttpClient() // No need to dispose HttpClient
    
    client.BaseAddress = new Uri(BaseURI);
    client.DefaultRequestHeaders.Accept.Clear();
    
    client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
    client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/xml"));
    
    var token = await HttpRuntime.Cache.GetToken();
    client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("XXX", token);
    
    var httpResponseMessage = await client.PostAsXmlAsync<TReq>("This/That/", req);
    if (!httpResponseMessage.IsSuccessStatusCode)
    {
       return Task.FromResult(new ErrorResponse()) // Create some kind of error response to indicate failure
    }
    
    var resp = await httpResponseMessage.Content.ReadAsAsync<TResp>();
    return resp;
    
  2. 创建一个ErrorLoggingHandler,它可以记录从web api收到的异常(或做其他事情):

    public class ErrorLoggingHandler : DelegatingHandler
    {
       private readonly StreamWriter _writer; // As a sample, log to a StreamWriter
    
       public ErrorLoggingHandler(Stream stream)
       {
          _writer = new StreamWriter(stream);
       }
    
       protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
       {
          var response = await base.SendAsync(request, cancellationToken);
    
          if (!response.IsSuccessStatusCode)
          {
              // This would probably be replaced with real error 
              // handling logic (return some kind of special response etc..)
              _writer.WriteLine("{0}\t{1}\t{2}", request.RequestUri,
                (int) response.StatusCode, response.Headers.Date);
          }
    
          return response;
       }
    
       protected override void Dispose(bool disposing)
       {
          if (disposing)
          {
             _writer.Dispose();
          }
    
          base.Dispose(disposing);
       }
    }
    
  3. 然后,您可以使用HttpClient创建HttpClientFactory

    var httpclient = HttpClientFactory.Create(new ErrorLoggingHandler(new FileStream(@"Location", FileMode.OpenOrCreate)));