【问题标题】:dealing with an error from a typed WebAPI HttpClient call处理来自类型化 WebAPI HttpClient 调用的错误
【发布时间】:2014-06-09 12:12:54
【问题描述】:

我对返回任务的 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 会是什么样子并会做什么来向调用控制器返回有用的东西...?

非常感谢

自然

【问题讨论】:

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


    【解决方案1】:

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

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

    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. 创建一个可以记录从 web api 接收到的异常(或执行其他操作)的 ErrorLoggingHandler:

      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);
         }
      }
      

    然后,您可以使用HttpClientFactory 创建您的HttpClient:

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

    【讨论】:

    • 嗨,感谢您的回复,但我无法从 GetMyThingAsync 方法返回我输入的响应(我认为 - 可能是错误的)以外的任何内容,因为它只返回一个 TResp(或其他),所以怎么能我让它返回一个 ErrorResponse ?同样使用 DelegatingHandler,我可以记录内容,但是如何在不引发异常的情况下将内容返回到控制器?
    • TResp 是什么类型?它包含什么?还有,你为什么要绑定它?控制器是否正在等待您返回有效响应?
    • 嗨 Yuval,也许愚蠢的是,我所有的调用都从方法本身返回了类型化的响应。所以对 GetAvailability 方法的调用将返回一个 AvailabilityResponse 对象。我应该创建一个更通用的客户端吗?如果它没有向我返回一个有故障的通用 HttpMessageResponse ,可以在稍后进行剖析..?我想使用 DelgatingHandlers 来处理添加授权标头,而不是为每种不同的方法复制它。所以也许一些通用的东西会更合适
    • 基本上我有许多 API 调用要进行,所有调用都返回不同的对象,我为其创建了类,我为每个对象编写了一个方法,返回一个类型化的结果
    猜你喜欢
    • 2019-04-02
    • 1970-01-01
    • 2020-07-10
    • 1970-01-01
    • 1970-01-01
    • 2014-03-11
    • 2016-02-07
    • 1970-01-01
    • 2010-09-29
    相关资源
    最近更新 更多