Почему в Windows.Web.Http.HttpClient возникают исключения для сети типа System.Exception, а не что-то более конкретное?
Всякий раз, когда я использую класс Windows.Web.Http.HttpClient для выполнения HTTP-запросов, я всегда обрабатываю сетевые исключения, например:
HttpResponseMessage response;
try
{
response = await httpClent.GetAsync(new Uri("http://www.microsoft.com"));
}
catch (Exception e)
{
// Most likely a network exception.
// Inspect e.HResult value to see what the specific error was.
}
Но теперь я поймаю все исключения, а не только сетевые исключения, особенно если блок try охватывает больше, чем просто httpClient.GetAsync
вызов.
Различные исключения HRESULT уже автоматически преобразуются в соответствующие управляемые типы на уровне ABI (например, E_OUTOFMEMORY проецируется в System.OutOfMemoryException), так почему же исключения сети не проецируются аналогичным образом?
3 ответа
Существует очень небольшое количество типов исключений, определенных WinRT, и ограниченное число HRESULT
s, которые будут проектироваться специально в C#.
В общем, шаблон проектирования WinRT API позволяет избежать исключений для всего, кроме вещей, которые являются ошибками программирования и должны быть обнаружены во время разработки (недопустимые аргументы, отсутствующие возможности и т. Д.) Или вещей, которые вы не можете реально восстановить (например, внешние из-памяти). Вы должны избегать обработки этих типов исключений с try
\ catch
потому что они представляют ошибки в вашем приложении или неспособность системы продолжать работу вашего приложения.
Вместо этого WinRT предпочитает, чтобы методы выполнялись успешно, но возвращают объекты с кодами состояния в них (например, ResponseCode
) что вы можете запросить, чтобы увидеть, успешно ли завершен метод или нет.
Это объясняется тем, что многие разработчики не могут обрабатывать исключения (из-за неполного тестирования своего приложения в различных конфигурациях). Необработанное исключение гарантированно приведет к остановке процесса, что не очень удобно для клиентов, но возвращаемое значение, указывающее на то, что сбой часто может обрабатываться приложениями либо потому, что они уже проверяли состояние по другим причинам (например, вы вероятно, всегда хотите проверить состояние HTTP, независимо от того, получили вы ошибку или нет) или потому, что код уже устойчив к "пустым" результатам (например, foreach
над пустым списком четко определен).
Не все API-интерфейсы следуют этому шаблону, особенно те, которые были разработаны на ранних этапах Windows 8, но это шаблон, который вы должны увидеть в большинстве WinRT-API. Вы также заметите много Try
API в стиле WinRT, которые пытаются что-то сделать и вернуть true
или же false
вместо того, чтобы бросать исключение. Так что по большей части ваш код должен быть свободным от try
/ catch
блокирует вызовы WinRT API, хотя вам все равно может понадобиться использовать их для собственного кода или сторонних библиотек.
Я не знаю почему Windows.Web.Http.HttpClient
Исключения классов не включаются автоматически в соответствующие управляемые типы, но (к счастью!) есть метод, который позволяет выяснить фактическую причину - Windows.Web.WebError.GetStatus.
Например:
using (var client = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Get, new Uri("http://www.example.com"));
try
{
// Http-errors are returned in the response, and no exception is thrown.
HttpResponseMessage response = await client.SendRequestAsync(request);
}
catch (Exception ex)
{
WebErrorStatus error = WebError.GetStatus(ex.HResult);
// For example, if your device could not connect to the internet at all,
// the error would be WebErrorStatus.HostNameNotResolved.
}
}
Что бы это ни стоило, я немного пытался решить, как обрабатывать ошибки (особенно связанные с сетью) при использовании Windows.Web.Http.HttpClient в приложениях UWP.
Шаблон, на котором я остановился, должен был вернуть объект (который я могу вернуть с помощью Задачи), который содержит либо информацию, либо исключение:
private class MyResponseObject
{
public string Data = string.Empty;
// Alternatively you could return the HttpResponseMessage (I guess).
//public HttpResponseMessage HttpResponseMessage;
public Exception Exception = null;
}
И, в частности, чтобы использовать проверку свойства IsSuccessStatusCode ответов сразу после получения ответа:
private async Task<MyResponseObject> CallService(Uri url)
{
MyResponseObject r = new MyResponseObject();
try
{
HttpResponseMessage response = await httpClient.GetAsync(url);
if (response.IsSuccessStatusCode)
{
// do something with the information successfully received
r.Data = await response.Content.ReadAsStringAsync();
}
}
catch (Exception ex)
{
// do something with the exception
r.Exception = ex;
}
return r;
}