使用 HttpClient.GetFromJsonAsync(),如何处理基于 HttpStatusCode 的 HttpRequestException 而无需额外的 SendAsync 调用?
Posted
技术标签:
【中文标题】使用 HttpClient.GetFromJsonAsync(),如何处理基于 HttpStatusCode 的 HttpRequestException 而无需额外的 SendAsync 调用?【英文标题】:Using HttpClient.GetFromJsonAsync(), how to handle HttpRequestException based on HttpStatusCode without extra SendAsync calls? 【发布时间】:2020-12-20 18:13:06 【问题描述】:System.Net.Http.Json
的HttpClient
扩展方法如GetFromJsonAsync()
大大简化了从Web API 检索json 对象的例程代码。使用起来很愉快。
但由于它的设计方式(直接返回反序列化的对象),它不会产生任何HttpResponseMessage
供检查,允许我根据HttpStatusCode
采取自定义操作。
相反,不成功的状态代码会导致HttpRequestException
,它似乎不提供任何暴露强类型HttpStatusCode
的属性。相反,状态代码包含在异常的Message
字符串本身中。
编辑:.NET 5.0 添加了HttpRequestException.StatusCode
属性,因此现在可以在调用GetFromJsonAsync
时对其进行检查。
//下面的旧帖
所以我一直在做这样的事情:
try
var cars = await httpClient.GetFromJsonAsync<List<Car>>("/api/cars");
//...
catch (HttpRequestException ex)
if (ex.Message.Contains(HttpStatusCode.Unauthorized.ToString()))
//Show unauthorized error page...
//...
这感觉有点hacky。使用创建HttpRequestMessage
和调用SendAsync
的老派方式,我们自然有机会检查回复的HttpResponseMessage.StatusCode
。添加其中一些代码会破坏使用System.Net.Http.Json
中的单行代码的便利目的。
如有任何建议,我们将不胜感激。
【问题讨论】:
我发现 .GetFromJsonAsyncSystem.Net.Http.HttpRequestException Response status code does not indicate success: 400 (Bad Request). at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
HttpClientExtensions.PostAsJsonAsync(HttpClientInstance, requestUri, body, cancellationToken);
【参考方案1】:
你可以使用:
// 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
我使用这样的基类:
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);
然后从派生类,我们回到单线
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 返回
遇到了 WebAPI 返回 null 的有效场景,行:
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 添加了 HttpRequestException.StatusCode 属性。使用的是 .NET Core 3.1,所以我不知道。GetAsync
在 .NET 5 Blazor 应用程序中不接受类型参数,因此建议的代码对我不起作用。
@Ted ... 请注意,从 MySpecificAPI(我使用类型)调用的 GetAsync 方法不是 HttpClient.GetAsync,它是我的基类 ClientAPI 中的 GetAsync 方法。反过来 ClientAPI 调用 HttpClient.GetAsync 方法(不支持类型参数)。上面的代码肯定有效,或者我的应用程序中有一些奇怪的魔法;-)
@Ted 很好地解决了 null 处理问题,你救了我的命!【参考方案2】:
我刚刚发现.NET 5.0 实际上将StatusCode
属性添加到HttpRequestException
类!
https://github.com/dotnet/runtime/pull/32455
https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httprequestexception.statuscode?view=net-5.0
【讨论】:
以上是关于使用 HttpClient.GetFromJsonAsync(),如何处理基于 HttpStatusCode 的 HttpRequestException 而无需额外的 SendAsync 调用?的主要内容,如果未能解决你的问题,请参考以下文章
在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?
Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)