http 상태 코드 400 (잘못된 요청)이 반환되면 .Net HttpWebRequest.GetResponse ()에서 예외가 발생 함


205

서버에서 HTTP 400 코드를 얻을 때 서버가 요청에 무엇이 잘못되었는지 알려주는 완전히 합법적 인 방법입니다 (HTTP 응답 내용의 메시지 사용)

그러나 상태 코드가 400이면 .NET HttpWebRequest에서 예외가 발생합니다.

이것을 어떻게 처리합니까? 나에게 400은 완전히 합법적이며 오히려 도움이된다. HTTP 내용에는 중요한 정보가 있지만 예외로 인해 경로에서 벗어날 수 있습니다.


6
나는 똑같은 일을 경험했다. .NET Framework 팀에 제안을 제출했습니다. 투표
해주십시오

답변:


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
WebException에 연결된 응답에서 GetResponseStream ()에 의해 반환 된 스트림의 내용은 서버가 실제로 반환 한 응답이 아니라 상태 코드의 이름 (예 : "잘못된 요청")입니다. 이 정보를 얻을 수있는 방법이 있습니까?
Mark Watts

@ MarkWatts : 서버가 반환 한 모든 것이 어야 하며, 내가 본 모든 상황에있었습니다. 특정 외부 URL로이를 재현 할 수 있습니까? 나는 당신이 새로운 질문을하고 (이것에 대한 언급) 무슨 일이 일어나고 있는지 보여줄 것을 제안합니다.
Jon Skeet

응답의 내용 길이가 0 일 때만이 작업이 수행됩니다. HTTP 상태 코드에 대한 텍스트 설명을 추가합니다. 400은 "잘못된 요청"이지만 다른 일부는 더 설명 적입니다.
Mark Watts

이 클래스에 대한 멋진 래퍼를 아는 사람이 있으면 여기에 대한 링크를 삭제하십시오. System.Net.WebClient는 예외 처리 시스템을 호출하여 동일한 방식으로 작동합니다.
John K

1
@AnkushJain : 2XX 동안 정상적으로 돌아올 것이라고 믿습니다. 구성에 따라 3XX에 대한 리디렉션 발생할 수 있습니다. 잘못 될 수는 있지만 예외가 발생할 것으로 예상됩니다. (4XX의 경우 인증 정보가 있지만 아직 사용하지 않은 경우 인증 정보를 적용 할 수 있습니다.)
Jon Skeet

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대신 대신 사용해야 하고 훨씬 더 구성 가능하며 미래의 방법이라고 생각합니다.
Matthew

request == null 확인이 실제로 필요합니까? 이것은 확장 메소드이므로 null 객체에서 사용하려고하면 확장 메소드 코드에 도달하기 전에 null 참조 예외를 throw해야합니다 ...
kwill

@kwill 확장 메소드는 정적 메소드 일뿐입니다. 특히 확장 메소드 구문으로 호출되지 않는 경우 혼동을 피하기 위해 적절한 입력 유효성 검증을 수행해야합니다. 또한 이것은 .NET 팀이 수행하는 일관된 접근 방식입니다 : github.com/dotnet/corefx/blob/…
Matthew

1
두 번째 질문을 잘못 이해해서 죄송합니다. ((WebRequest) null).GetResponseWithoutException()는에 NullReferenceException해당하는로 컴파일되기 때문에 실제로는을 유발 WebRequestExtensions.GetResponseWithoutException(null)하지 NullReferenceException않으므로 입력 유효성 검사가 필요 하지 않습니다 .
마태 복음

13

흥미롭게도 HttpWebResponse.GetResponseStream() 얻는 WebException.Response것은 서버에서받은 응답 스트림과 동일하지 않습니다. 우리 환경에서는 400 HTTP 상태 코드가 HttpWebRequest/HttpWebResponse객체를 사용하여 클라이언트에 다시 반환 될 때 실제 서버 응답이 손실 됩니다. 우리가 본 것에서,와 관련된 응답 스트림 WebException's HttpWebResponse은 클라이언트에서 생성되며 서버의 응답 본문을 포함하지 않습니다. 잘못된 요청의 이유를 고객에게 다시 메시지로 보내려고하므로 매우 실망 스럽습니다.


HEAD 메소드를 사용 하여이 예외가 발생하지만 GET을 사용할 때 아무런 문제가 없습니다. HEAD 방법의 문제점은 정확히 무엇입니까?
Mohammad Afrashteh

12

Google OAuth2 서비스에 연결하려고 할 때 비슷한 문제가 발생했습니다.

다음과 같이 WebRequest를 사용하지 않고 수동으로 POST를 작성했습니다.

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 개의 컨텐츠를 "잘못된 요청"으로 변경합니다.
솔루션 : 이 문제 를 해결하기 위해
추가 ; 이제 모든 서버 (로컬 및 원격)는 내가 반환하는 IP 주소 및 / 또는 HTTP 코드에 관계없이 정확히 동일한 내용 (내가 생성 한)을 반환합니다.<httpErrors existingResponse="PassThrough"></httpErrors>web.config/configuration/system.webServer

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.