#c# #asp.net-core #asp.net-web-api #dotnet-httpclient
#c# #asp.net-core #asp.net-web-api #dotnet-httpclient
Вопрос:
System.Net.Http.Json
, HttpClient
таких как GetFromJsonAsync()
значительно упрощает обычные коды для извлечения объектов json из веб-API. Это приятно использовать.
Но из-за того, как он разработан (возвращает десериализованные объекты напрямую), он не выдает никаких HttpResponseMessage
для проверки, что позволяет мне выполнять пользовательские действия на основе HttpStatusCode
.
Вместо этого коды состояния неуспешности приводят к a 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...
}
//...
}
Это кажется немного хакерским. Используя старый школьный способ создания HttpRequestMessage
и вызова SendAsync
, мы, естественно, получили возможность проверить ответ HttpResponseMessage.StatusCode
. Добавление некоторых из этих кодов обратно лишило бы удобной цели использования однострочников System.Net.Http.Json
.
Любые предложения здесь были бы весьма признательны.
Комментарии:
1. Я нашел это. GetFromJsonAsync<T> выдает исключение на основе StatusCode:
System.Net.Http.HttpRequestException Response status code does not indicate success: 400 (Bad Request). at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
2. wrt мой предыдущий комментарий, нашел этот подход:
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
}
ОБНОВЛЕНИЕ: ОБРАБОТКА НУЛЕВЫХ ВОЗВРАТОВ
Столкнувшись с допустимым сценарием, в котором WebAPI возвращает null, строка:
return await res.Content.ReadFromJsonAsync<TReturn>();
выдаст ошибку десериализации Json.
Чтобы решить эту проблему, нам нужно обнаружить NoContent response (204) и обработать соответствующим образом:
if (res.StatusCode == HttpStatusCode.NoContent)
return default(TReturn);
else if (res.IsSuccessStatusCode)
return await res.Content.ReadFromJsonAsync<TReturn>();
Комментарии:
1. На самом деле, я также только что узнал, что .NET 5.0 добавил HttpRequestException . Свойства StatusCode. Использовал .NET Core 3.1, поэтому я не знал.
2.
GetAsync
не принимает аргумент типа в приложении .NET 5 Blazor, поэтому предлагаемый код для меня не работает.3. @Ted … Обратите внимание, что метод GetAsync, вызываемый из MySpecificAPI (где я использую type ), не является HttpClient . GetAsync, это метод GetAsync в моем базовом классе ClientAPI. В свою очередь ClientAPI вызывает HttpClient. метода GetAsync (который не поддерживает аргумент типа). Приведенный выше код определенно работает, или в моем приложении происходит какая-то странная магия 😉
4. @Ted Хороший улов по проблеме с обработкой null, вы спасли мой день!
Ответ №2:
Я только что узнал, что .NET 5.0 фактически добавил StatusCode
свойство в HttpRequestException
класс!