.Net HttpWebRequest.GetResponse() вызывает исключение, когда возвращается код состояния http 400 (неверный запрос).

StackOverflow https://stackoverflow.com/questions/692342

  •  22-08-2019
  •  | 
  •  

Вопрос

Я нахожусь в ситуации, когда, когда я получаю код HTTP 400 от сервера, это совершенно законный способ, с помощью которого сервер сообщает мне, что не так с моим запросом (используя сообщение в содержимом ответа HTTP).

Однако .NET HttpWebRequest вызывает исключение, когда код состояния равен 400.

Как мне с этим справиться?Для меня 400 абсолютно легальны и весьма полезны.Содержимое HTTP содержит важную информацию, но исключение сбило меня с пути.

Это было полезно?

Решение

Было бы неплохо, если бы был какой-то способ отключить «выброс кода при неудачном завершении», но если вы поймаете WebException, вы можете хотя бы использовать ответ:

using System;
using System.IO;
using System.Web;
using System.Net;

public class Test
{
    static void Main()
    {
        WebRequest request = WebRequest.Create("http://csharpindepth.com/asd");
        try
        {
            using (WebResponse response = request.GetResponse())
            {
                Console.WriteLine("Won't get here");
            }
        }
        catch (WebException e)
        {
            using (WebResponse response = e.Response)
            {
                HttpWebResponse httpResponse = (HttpWebResponse) response;
                Console.WriteLine("Error code: {0}", httpResponse.StatusCode);
                using (Stream data = response.GetResponseStream())
                using (var reader = new StreamReader(data))
                {
                    string text = reader.ReadToEnd();
                    Console.WriteLine(text);
                }
            }
        }
    }
}

Возможно, вы захотите инкапсулировать бит «получите мне ответ, даже если это не код успеха» в отдельный метод.(Я бы посоветовал вам все равно бросить, если нет ответа, например.если не удалось подключиться.)

Если ответ на ошибку может быть большим (что необычно), вы можете настроить HttpWebRequest.DefaultMaximumErrorResponseLength чтобы убедиться, что вы получили всю ошибку.

Другие советы

Я знаю, что на этот вопрос уже давно был дан ответ, но я создал метод расширения, чтобы, надеюсь, помочь другим людям, которые задаются этим вопросом.

Код:

public static class WebRequestExtensions
{
    public static WebResponse GetResponseWithoutException(this WebRequest request)
    {
        if (request == null)
        {
            throw new ArgumentNullException("request");
        }

        try
        {
            return request.GetResponse();
        }
        catch (WebException e)
        {
            if (e.Response == null)
            {
                throw;
            }

            return e.Response;
        }
    }
}

Использование:

var request = (HttpWebRequest)WebRequest.CreateHttp("http://invalidurl.com");

//... (initialize more fields)

using (var response = (HttpWebResponse)request.GetResponseWithoutException())
{
    Console.WriteLine("I got Http Status Code: {0}", response.StatusCode);
}

Интересно, HttpWebResponse.GetResponseStream() что вы получаете от WebException.Response это не то же самое, что поток ответов, который вы получили бы от сервера.В нашей среде мы теряем реальные ответы сервера, когда 400 HTTP-статус код возвращается обратно клиенту с помощью HttpWebRequest/HttpWebResponse объекты.Судя по тому, что мы видели, поток ответов, связанный с WebException's HttpWebResponse генерируется на клиенте и не включает в себя тело ответа от сервера.Очень неприятно, поскольку мы хотим сообщить клиенту причину неправильного запроса.

У меня были аналогичные проблемы при попытке подключиться к службе Google OAuth2.

В итоге я написал POST вручную, не используя WebRequest, вот так:

TcpClient client = new TcpClient("accounts.google.com", 443);
Stream netStream = client.GetStream();
SslStream sslStream = new SslStream(netStream);
sslStream.AuthenticateAsClient("accounts.google.com");

{
    byte[] contentAsBytes = Encoding.ASCII.GetBytes(content.ToString());

    StringBuilder msg = new StringBuilder();
    msg.AppendLine("POST /o/oauth2/token HTTP/1.1");
    msg.AppendLine("Host: accounts.google.com");
    msg.AppendLine("Content-Type: application/x-www-form-urlencoded");
    msg.AppendLine("Content-Length: " + contentAsBytes.Length.ToString());
    msg.AppendLine("");
    Debug.WriteLine("Request");
    Debug.WriteLine(msg.ToString());
    Debug.WriteLine(content.ToString());

    byte[] headerAsBytes = Encoding.ASCII.GetBytes(msg.ToString());
    sslStream.Write(headerAsBytes);
    sslStream.Write(contentAsBytes);
}

Debug.WriteLine("Response");

StreamReader reader = new StreamReader(sslStream);
while (true)
{  // Print the response line by line to the debug stream for inspection.
    string line = reader.ReadLine();
    if (line == null) break;
    Debug.WriteLine(line);
}

Ответ, который записывается в поток ответов, содержит конкретный текст ошибки, который вам нужен.

В частности, моя проблема заключалась в том, что я помещал конечные строки между фрагментами данных, закодированными в URL-адресе.Когда я их вынул, все заработало.Возможно, вы сможете использовать аналогичный метод для подключения к вашей службе и чтения фактического текста ошибки ответа.

Попробуйте это (это VB-код :-):

Try

Catch exp As WebException
  Dim sResponse As String = New StreamReader(exp.Response.GetResponseStream()).ReadToEnd
End Try

Асинхронная версия функции расширения:

    public static async Task<WebResponse> GetResponseAsyncNoEx(this WebRequest request)
    {
        try
        {
            return await request.GetResponseAsync();
        }
        catch(WebException ex)
        {
            return ex.Response;
        }
    }

Это решило это для меня:
https://gist.github.com/beccasaurus/929007/a8f820b153a1cfdee3d06a9c0a1d7ebfced8bb77

ТЛ;ДР:
Проблема:
localhost возвращает ожидаемый контент, удаленный IP изменяет контент 400 на «Неверный запрос».
Решение:
Добавление <httpErrors existingResponse="PassThrough"></httpErrors> к web.config/configuration/system.webServer решил это для меня;теперь все серверы (локальные и удаленные) возвращают один и тот же контент (сгенерированный мной) независимо от IP-адреса и/или HTTP-кода, который я возвращаю.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top