.Net HttpWebRequest.GetResponse () levanta exceção quando http código de status 400 (Bad Request) é devolvido
-
22-08-2019 - |
Pergunta
Eu estou em uma situação onde, quando eu recebo um código HTTP 400 do servidor, é uma maneira completamente legal do servidor me dizer o que estava errado com o meu pedido (através de uma mensagem no conteúdo da resposta HTTP)
No entanto, a NET HttpWebRequest levanta uma exceção quando o código de status é de 400.
Como posso lidar com isso? Para mim um 400 é completamente legal e bastante útil. O conteúdo HTTP tem alguma informação importante, mas a exceção me joga fora de meu caminho.
Solução
Seria bom se houvesse alguma maneira de desligar "jogar em código não-sucesso", mas se você pegar WebException você pode pelo menos usar a resposta:
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);
}
}
}
}
}
Você pode gostar de encapsular o "get me uma resposta, mesmo se não é um código de sucesso" bit em um método separado. (Eu sugiro que você ainda jogar se não houver uma resposta, por exemplo, se você não pôde se conectar.)
Se a resposta de erro pode ser grande (o que é incomum), você pode querer ajustar HttpWebRequest.DefaultMaximumErrorResponseLength
para ter certeza de obter toda a erro.
Outras dicas
Eu sei que isso já foi respondido há muito tempo, mas eu fiz um método de extensão para espero ajudar outras pessoas que vêm a esta questão.
Código:
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;
}
}
}
Uso:
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);
}
Curiosamente, o HttpWebResponse.GetResponseStream()
que você começa a partir do WebException.Response
não é o mesmo que o fluxo de resposta que você teria recebido do servidor. Em nosso meio, estamos perdendo respostas do servidor reais quando um 400 status HTTP código é devolvido de volta ao cliente usando os objetos HttpWebRequest/HttpWebResponse
. Pelo que vimos, o fluxo de resposta associado ao WebException's HttpWebResponse
é gerado no cliente e não inclui qualquer do corpo resposta do servidor. Muito frustrante, pois queremos mensagem de volta ao cliente a razão para o mau pedido.
Eu tive problemas semelhantes ao tentar conectar ao serviço OAuth2 do Google.
acabei escrevendo o POST manualmente, sem usar WebRequest, como este:
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);
}
A resposta que obtém escrito para o fluxo de resposta contém o texto de erro específico que você está depois.
Em particular, o meu problema era que eu estava colocando linhas finais entre as peças de dados codificado em URL. Quando assumi-los, tudo funcionou. Você pode ser capaz de usar uma técnica semelhante para se conectar ao seu serviço e ler o texto de erro de resposta real.
Tente este (é VB-Code: -):
Try
Catch exp As WebException
Dim sResponse As String = New StreamReader(exp.Response.GetResponseStream()).ReadToEnd
End Try
Uma versão assíncrona da função de extensão:
public static async Task<WebResponse> GetResponseAsyncNoEx(this WebRequest request)
{
try
{
return await request.GetResponseAsync();
}
catch(WebException ex)
{
return ex.Response;
}
}
Este resolvido para mim:
https://gist.github.com/beccasaurus/929007/a8f820b153a1cfdee3d06a9c0a1d7ebfced8bb77
TL; DR:
Problema:
localhost retornos conteúdo esperado, altera IP remotos 400 conteúdo para "Bad Request"
Solução:
Adicionando <httpErrors existingResponse="PassThrough"></httpErrors>
para web.config/configuration/system.webServer
resolvido isso por mim; agora todos os servidores (local e remoto) retornam o mesmo conteúdo exato (gerado por mim) independentemente do endereço IP e / ou HTTP código que eu retorno.