.Net HttpWebRequest.GetResponse () tăng ngoại lệ khi trả về mã trạng thái http 400 (yêu cầu xấu)


205

Tôi đang ở trong tình huống khi tôi nhận được mã HTTP 400 từ máy chủ, đó là cách hoàn toàn hợp pháp của máy chủ cho tôi biết điều gì sai với yêu cầu của tôi (sử dụng tin nhắn trong nội dung phản hồi HTTP)

Tuy nhiên, .NET HttpWebRequest đưa ra một ngoại lệ khi mã trạng thái là 400.

Làm thế nào để tôi xử lý này? Đối với tôi 400 là hoàn toàn hợp pháp, và khá hữu ích. Nội dung HTTP có một số thông tin quan trọng nhưng ngoại lệ khiến tôi không thể thực hiện được.


6
Tôi đã trải nghiệm điều tương tự. Tôi đã gửi một đề nghị cho nhóm .NET Framework. Hãy bình chọn cho nó: connect.microsoft.com/VisualStudio/feedback/details/575075/
mẹo

Câu trả lời:


344

Sẽ thật tuyệt nếu có một số cách tắt "mã không thành công" nhưng nếu bạn bắt được WebException, ít nhất bạn có thể sử dụng phản hồi:

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

Bạn có thể gói gọn bit "lấy cho tôi một phản hồi ngay cả khi đó không phải là mã thành công" trong một phương thức riêng biệt. (Tôi đề nghị bạn vẫn ném nếu không có phản hồi, ví dụ: nếu bạn không thể kết nối.)

Nếu phản hồi lỗi có thể lớn (không bình thường), bạn có thể muốn điều chỉnh HttpWebRequest.DefaultMaximumErrorResponseLengthđể đảm bảo rằng bạn nhận được toàn bộ lỗi.


5
Nội dung của luồng được GetResponseStream () trả về trên phản hồi được đính kèm với WebException chỉ là tên của mã trạng thái (ví dụ: "Yêu cầu xấu") chứ không phải là phản hồi thực sự được máy chủ trả về. Có cách nào để có được thông tin này?
Đánh dấu Watts

@MarkWatts: Nó phải là bất cứ thứ gì được máy chủ trả về và đã ở trong mọi tình huống tôi từng thấy. Bạn có thể tái tạo điều này với một URL bên ngoài cụ thể không? Tôi đề nghị bạn hỏi một câu hỏi mới (đề cập đến câu hỏi này) và cho thấy những gì đang diễn ra.
Jon Skeet

Hóa ra nó chỉ làm điều này khi độ dài nội dung của phản hồi bằng không; nó thêm một mô tả văn bản về mã trạng thái HTTP - 400 chỉ là "Yêu cầu xấu" nhưng một số khác mô tả nhiều hơn.
Đánh dấu Watts

Nếu bất cứ ai biết về một trình bao bọc đẹp cho lớp này, hãy thả một liên kết đến nó ở đây. System.Net.WebClient hành xử theo cùng một cách bằng cách gọi hệ thống xử lý ngoại lệ.
John K

1
@AnkushJain: Tôi tin rằng nó sẽ trở lại bình thường cho 2XX; chuyển hướng có thể xảy ra cho 3XX tùy thuộc vào cấu hình - Tôi hy vọng mọi thứ khác sẽ gây ra ngoại lệ, mặc dù tôi có thể sai. (Đối với 4XX, có thể sau đó nó sẽ áp dụng thông tin xác thực nếu có nhưng chưa sử dụng.)
Jon Skeet

48

Tôi biết điều này đã được trả lời từ lâu, nhưng tôi đã thực hiện một phương pháp mở rộng để hy vọng giúp đỡ những người khác đến với câu hỏi này.

Mã số:

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

Sử dụng:

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.Responsecó thể và có thể null. Bạn nên suy nghĩ lại nếu đây là trường hợp.
Ian Kemp

@DavidP Tôi đồng ý, việc sử dụng hơi kỳ quặc, mặc dù đối với hầu hết mọi thứ, có lẽ bạn nên sử dụng HttpClientthay vào đó, nó có thể cấu hình hơn nhiều và tôi tin rằng đó là cách của tương lai.
Matthew

Việc kiểm tra yêu cầu == null có thực sự cần thiết? Vì đây là một phương thức mở rộng, nên cố gắng sử dụng nó trên một đối tượng null nên ném ngoại lệ tham chiếu null trước khi nó chạm vào mã phương thức mở rộng ...... phải không?
kwill

@kwill Các phương thức mở rộng chỉ là các phương thức tĩnh, thực hiện xác thực đầu vào thích hợp để tránh nhầm lẫn, đặc biệt là khi chúng không được gọi bằng cú pháp phương thức mở rộng. Ngoài ra, đây là cách tiếp cận nhất quán được thực hiện bởi các nhóm .NET: github.com/dotnet/corefx/blob/ mẹo
Matthew

1
Xin lỗi tôi đã hiểu nhầm câu hỏi thứ hai của bạn. ((WebRequest) null).GetResponseWithoutException()trong thực tế sẽ không gây ra a NullReferenceException, vì nó được biên dịch tương đương với WebRequestExtensions.GetResponseWithoutException(null), điều này sẽ không dẫn đến a NullReferenceException, do đó cần phải xác nhận đầu vào.
Ma-thi-ơ

13

Thật thú vị, những HttpWebResponse.GetResponseStream()gì bạn nhận được từ WebException.Responsekhông giống như luồng phản hồi mà bạn sẽ nhận được từ máy chủ. Trong môi trường của chúng tôi, chúng tôi đang mất phản hồi của máy chủ thực tế khi mã trạng thái 400 HTTP được trả lại cho máy khách bằng cách sử dụng các HttpWebRequest/HttpWebResponseđối tượng. Từ những gì chúng ta đã thấy, luồng phản hồi được liên kết với máy khách WebException's HttpWebResponseđược tạo tại máy khách và không bao gồm bất kỳ nội dung phản hồi nào từ máy chủ. Rất bực bội, vì chúng tôi muốn nhắn lại cho khách hàng lý do cho yêu cầu xấu.


Tôi sử dụng phương thức HEAD và gây ra ngoại lệ này nhưng khi sử dụng GET thì không có vấn đề gì. Vấn đề với phương pháp CHÍNH là gì?
Mohammad Afrashteh

12

Tôi gặp vấn đề tương tự khi cố gắng kết nối với dịch vụ OAuth2 của Google.

Cuối cùng tôi đã viết POST bằng tay, không sử dụng WebRequest, như thế này:

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

Phản hồi được ghi vào luồng phản hồi chứa văn bản lỗi cụ thể mà bạn đang theo dõi.

Cụ thể, vấn đề của tôi là tôi đã đặt các điểm cuối giữa các phần dữ liệu được mã hóa url. Khi tôi lấy chúng ra, mọi thứ đều hoạt động. Bạn có thể sử dụng một kỹ thuật tương tự để kết nối với dịch vụ của mình và đọc văn bản lỗi phản hồi thực tế.


2
HttpWebRequest rất rối. Ngay cả các ổ cắm cũng dễ dàng hơn (vì chúng không che giấu lỗi của bạn).
Đặc vụ_L

6

Hãy thử điều này (đó là VB-Code :-):

Try

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

3

Một phiên bản không đồng bộ của chức năng mở rộng:

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

0

Điều này đã giải quyết nó cho tôi:
https://gist.github.com/beccasaurus/929007/a8f820b153a1cfdee3d06a9c0a1d7ebfced8bb77

TL; DR: Sự cố
:
localhost trả về nội dung dự kiến, IP từ xa thay đổi 400 nội dung thành
Giải pháp "Yêu cầu xấu" :
Thêm <httpErrors existingResponse="PassThrough"></httpErrors>để web.config/configuration/system.webServergiải quyết vấn đề này cho tôi; bây giờ tất cả các máy chủ (cục bộ & từ xa) trả về cùng một nội dung (do tôi tạo) bất kể địa chỉ IP và / hoặc mã HTTP tôi trả về.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.