.Net HttpWebRequest.GetResponse () збільшує виняток, коли повертається код статусу 400 (неправильний запит)


205

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

Однак .NET HttpWebRequest створює виняток, коли код статусу дорівнює 400.

Як мені впоратися з цим? Для мене 400 є цілком законним і досить корисним. Вміст HTTP містить важливу інформацію, але виняток відкидає мене з мого шляху.


6
Я переживав те саме. Я подав пропозицію команді .NET Framework. Не соромтесь голосувати за нього: connect.microsoft.com/VisualStudio/feedback/details/575075/…
Йонас Ставський

Відповіді:


344

Було б добре, якби був якийсь спосіб відключення "кинути на невдалий код", але якщо ви вловили 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щоб переконатися, що ви отримали всю помилку.


5
Вміст потоку, повернутого GetResponseStream () у відповіді, приєднаній до WebException, є лише назвою коду статусу (наприклад, "Неправильний запит"), а не відповіді, фактично повернутого сервером. Чи є спосіб отримати цю інформацію?
Марк Уоттс

@MarkWatts: Це повинно бути все, що повернуто сервером, і було в будь-якій ситуації, яку я бачив. Чи можете ви відтворити це за допомогою певної зовнішньої URL-адреси? Я пропоную вам задати нове запитання (маючи на увазі це) та показати, що відбувається.
Джон Скіт

Виявляється, це робиться лише тоді, коли довжина вмісту відповіді дорівнює нулю; він додає текстовий опис коду статусу HTTP - 400 - це лише "поганий запит", але деякі з них є більш описовими.
Марк Уоттс

Якщо хтось знає про гарну обгортку для цього класу, перекажіть тут посилання. System.Net.WebClient поводиться так само, викликаючи систему обробки винятків.
Джон К

1
@AnkushJain: я вірю, що вона повернеться нормально за 2XX; перенаправлення може статися для 3XX залежно від конфігурації - я б очікував, що все ще спричинить виняток, хоча я можу помилитися. (Для 4XX, можливо, він застосує автентифіковану інформацію, якщо вона є, але вона її ще не використала.)
Джон Скіт

48

Я знаю, що на це вже давно відповіли, але я застосував метод розширення, щоб сподіватися допомогти іншим, хто прийшов до цього питання.

Код:

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);
}

2
WebException.Responseможе і може бути null. Вам слід повторно скинутись, якщо це так.
Ян Кемп

@DavidP Я погоджуюся, використання є трохи примхливим, хоча для більшості речей, ви, ймовірно, повинні використовувати HttpClientзамість цього, це набагато більш настроюється, і я вважаю, що це шлях майбутнього.
Матвій

Чи потрібна перевірка запиту == null? Оскільки це метод розширення, намагаючись використовувати його на нульовому об'єкті, слід викинути нульове виняткове посилання, перш ніж він потрапить на код методу розширення ...... правда?
kwill

Методи розширення @kwill - це лише статичні методи, для правильного введення даних слід робити перевірку, щоб уникнути плутанини, особливо коли вони не викликаються синтаксисом методу розширення. Також це послідовний підхід команд .NET: github.com/dotnet/corefx/blob/…
Матвій

1
Вибачте, що я неправильно зрозумів ваше друге запитання. ((WebRequest) null).GetResponseWithoutException()насправді не призведе до NullReferenceException, оскільки він складений в еквіваленті WebRequestExtensions.GetResponseWithoutException(null), що не призведе до а NullReferenceException, отже, необхідність перевірки вводу.
Матвій

13

Цікаво, що те, HttpWebResponse.GetResponseStream()що ви отримуєте від WebException.Response, не те саме, що потік відповідей, який ви отримали б із сервера. У нашому середовищі ми втрачаємо реальні відповіді сервера, коли 400 код статусу HTTP повертається клієнту назад за допомогою HttpWebRequest/HttpWebResponseоб'єктів. З того, що ми бачили, потік відповідей, пов’язаний із WebException's HttpWebResponse, формується у клієнта і не включає жодного органу відповіді з сервера. Дуже засмучує, оскільки ми хочемо повернути клієнту причину поганого запиту.


Я використовую метод HEAD і викликаю це виняток, але при використанні GET немає жодних проблем. У чому полягає проблема з методом HEAD?
Мохаммед Афраштех

12

У мене виникли подібні проблеми при спробі підключитися до служби OAuth2 Google.

Я закінчив писати 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-адресами. Коли я їх виймав, все спрацювало. Можливо, ви зможете скористатися подібною технікою для підключення до вашої послуги та читання тексту фактичної помилки відповіді.


2
HttpWebRequest так заплутаний. Навіть розетки простіше (адже вони не приховують від вас помилок).
Agent_L

6

Спробуйте це (це VB-код :-):

Try

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

3

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

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

0

Це вирішило це для мене:
https://gist.github.com/beccasaurus/929007/a8f820b153a1cfdee3d06a9c0a1d7ebfced8bb77

TL; DR:
Проблема:
localhost повертає очікуваний контент, віддалений IP змінює 400 вмісту на "Bad Request"
Рішення:
Додавання <httpErrors existingResponse="PassThrough"></httpErrors>до web.config/configuration/system.webServerвирішеного для мене; тепер усі сервери (локальні та віддалені) повертають той самий вміст (згенерований мною) незалежно від IP-адреси та / або коду HTTP, який я повертаю.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.