使用 HttpClient.GetFromJsonAsync(),如何在没有额外 SendAsync 调用的情况下处理基于 HttpStatusCode 的 HttpRequestException?
Using HttpClient.GetFromJsonAsync(), how to handle HttpRequestException based on HttpStatusCode without extra SendAsync calls?
System.Net.Http.Json
的 HttpClient
扩展方法,例如 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!
System.Net.Http.Json
的 HttpClient
扩展方法,例如 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!