使用 HttpClient.GetFromJsonAsync(),如何在没有额外 SendAsync 调用的情况下处理基于 HttpStatusCode 的 HttpRequestException?

Using HttpClient.GetFromJsonAsync(), how to handle HttpRequestException based on HttpStatusCode without extra SendAsync calls?

System.Net.Http.JsonHttpClient 扩展方法,例如 GetFromJsonAsync() 大大简化了从 Web API 检索 json 对象的例程代码。用起来很开心

但是由于它的设计方式(直接返回反序列化对象),它不会产生任何 HttpResponseMessage 用于检查,允许我根据 HttpStatusCode.[=26= 采取自定义操作]

相反,非成功状态代码会导致 HttpRequestException,它似乎不提供任何公开强类型 HttpStatusCode 的属性。相反,状态代码包含在异常的 Message 字符串本身中。

编辑:.NET 5.0 添加了 HttpRequestException.StatusCode 属性,因此现在可以在调用 GetFromJsonAsync.[=26 时对其进行检查=]

//旧post低于

所以我一直在做这样的事情:

try
{
  var cars = await httpClient.GetFromJsonAsync<List<Car>>("/api/cars");
  //...
}
            
catch (HttpRequestException ex)
{
   if (ex.Message.Contains(HttpStatusCode.Unauthorized.ToString()))
   {
     //Show unauthorized error page...
   }
   //...
}

这感觉有点老套。使用创建 HttpRequestMessage 和调用 SendAsync 的老派方法,我们自然有机会检查响应的 HttpResponseMessage.StatusCode。将其中一些代码加回去会破坏在 System.Net.Http.Json.

中使用单行代码的方便目的

如有任何建议,我们将不胜感激。

您可以使用:

// return HttpResponseMessage
var res= await httpClient.GetAsync<List<Car>>("/api/cars")

if (res.IsSuccessStatusCode)
   var cars = res.Content.ReadFromJsonAsync<List<Car>>();
else
   // deal with the HttpResponseMessage directly as you used to

我使用这样的基 class:

using System;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;


namespace MyProject.ClientAPI
{
    public abstract class ClientAPI
    {
        protected readonly HttpClient Http;
        private readonly string BaseRoute;

        protected ClientAPI(string baseRoute, HttpClient http)
        {
            BaseRoute = baseRoute;
            Http = http;
        }

        protected async Task<TReturn> GetAsync<TReturn>(string relativeUri)
        {
            HttpResponseMessage res = await Http.GetAsync($"{BaseRoute}/{relativeUri}");
            if (res.IsSuccessStatusCode)
            {
                return await res.Content.ReadFromJsonAsync<TReturn>();
            }
            else
            {
                string msg = await res.Content.ReadAsStringAsync();
                Console.WriteLine(msg);
                throw new Exception(msg);
            }
        }
        
        protected async Task<TReturn> PostAsync<TReturn, TRequest>(string relativeUri, TRequest request)
        {
            HttpResponseMessage res = await Http.PostAsJsonAsync<TRequest>($"{BaseRoute}/{relativeUri}", request);
            if (res.IsSuccessStatusCode)
            {
                return await res.Content.ReadFromJsonAsync<TReturn>();
            }
            else
            {
                string msg = await res.Content.ReadAsStringAsync();
                Console.WriteLine(msg);
                throw new Exception(msg);
            }
        }
    }
}

然后从导出的class,我们又回到了单行

public class MySpecificAPI : ClientAPI
{
    public MySpecificAPI(HttpClient http) : base("api/myspecificapi", http) {}
    
    public async Task<IEnumerable<MyClass>> GetMyClassAsync(int ownerId)
    {
        try
        {
            return GetAsync<IEnumerable<MyClass>>($"apiMethodName?ownerId={ownerId}");
        }
        catch (Exception e)
        {
            // Deal with exception
        }
    }
    
    // repeat for post
}

更新:处理 NULL RETURNS

遇到 WebAPI returns 为空的有效场景,行:

return await res.Content.ReadFromJsonAsync<TReturn>();

将引发 Json 反序列化错误。

为了解决这个问题,我们需要检测 NoContent 响应 (204) 并进行相应处理:

if (res.StatusCode == HttpStatusCode.NoContent)
    return default(TReturn);
else if (res.IsSuccessStatusCode)
    return await res.Content.ReadFromJsonAsync<TReturn>();

我刚刚发现 .NET 5.0 实际上将 StatusCode 属性 添加到 HttpRequestException class!

https://github.com/dotnet/runtime/pull/32455

https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httprequestexception.statuscode?view=net-5.0