HTTP POST retorna erro: 417 "Expectation Failed".

212

Quando tento postar em um URL, resulta na seguinte exceção:

O servidor remoto retornou um erro: (417) Expectation Failed.

Aqui está um código de exemplo:

var client = new WebClient();

var postData = new NameValueCollection();
postData.Add("postParamName", "postParamValue");

byte[] responseBytes = client.UploadValues("http://...", postData);
string response = Encoding.UTF8.GetString(responseBytes); // (417) Expectation Failed.

Usar um HttpWebRequest/HttpWebResponsepar ou um HttpClientnão faz diferença.

O que está causando essa exceção?

Saeb Amini
fonte
2
O problema parece ocorrer quando seu aplicativo se comunica através de um servidor proxy. Um aplicativo .NET que escrevi funcionava quando estava diretamente conectado à Internet, mas não quando estava atrás de um servidor proxy.
Salman A
2
Observou essa condição quando um cliente está executando através de um servidor proxy HTTP 1.0 (somente). O cliente (proxy asmx sem nenhuma configuração) está enviando uma solicitação HTTP 1.1 e o proxy (antes que qualquer servidor possa se envolver) rejeita o que o proxy envia. Se um usuário final tiver esse problema, o uso da solução de configuração abaixo é uma solução apropriada, pois faria com que as solicitações fossem geradas sem depender do proxy que entendesse o Expectcabeçalho que, por padrão, é adicionado como Expect100Continueestá truepor padrão.
Ruben Bartelink

Respostas:

478

System.Net.HttpWebRequest adiciona o cabeçalho 'HTTP header "Expect: 100-Continue"' a cada solicitação, a menos que você solicite explicitamente que não, definindo esta propriedade estática como false:

System.Net.ServicePointManager.Expect100Continue = false;

Alguns servidores engasgam com esse cabeçalho e devolvem o erro 417 que você está vendo.

Dê uma chance a isso.

xcud
fonte
5
Eu tinha um código que falava com o Twitter que de repente parou de funcionar no dia seguinte ao Natal. Eles haviam feito uma atualização ou alteração na configuração que fazia com que seus servidores começassem a sufocar nesse cabeçalho. Foi uma dor encontrar a solução.
xcud 19/02/09
8
Acho que peguei 10 pontos extras para obter uma resposta aceita em um tópico no qual Jon Skeet postou uma solução.
Xcud
1
Obrigado! Isso deve ser observado em algum lugar nos exemplos de msdn
Eugene
25
Você também pode especificar essa propriedade em seu app.config: <system.net> <settings> <servicePointManager expect100Continue = "false" />. nahidulkibria.blogspot.com/2009/06/…
Andre Luus 29/04
2
Nota: essa configuração também se aplica a ServiceReferences e eu assumo WebReferences.
Myster
114

Outra maneira -

Adicione estas linhas à seção de configuração do arquivo de configuração do aplicativo:

<system.net>
    <settings>
        <servicePointManager expect100Continue="false" />
    </settings>
</system.net>
Engin Ardıç
fonte
6
Funciona muito bem quando você precisa fazer uma alteração operacional e não está pronto para uma alteração no código.
Davidebber #
1
O que essa linha de configuração faz?
J86
31

A mesma situação e erro também podem surgir com um proxy de serviço da Web SOAP gerado por um assistente padrão (não 100% se esse também for o caso na System.ServiceModelpilha do WCF ) quando em tempo de execução:

  • a máquina do usuário final está configurada (nas Configurações da Internet) para usar um proxy que não entende o HTTP 1.1
  • o cliente acaba enviando algo que um proxy HTTP 1.0 não entende (geralmente um Expectcabeçalho como parte de um HTTP POSTou PUTsolicitação devido a uma convenção de protocolo padrão de enviar a solicitação em duas partes, conforme coberto nas observações aqui )

... produzindo um 417.

Conforme abordado nas outras respostas, se o problema específico em que você se deparar for o fato de o Expectcabeçalho estar causando o problema, esse problema específico poderá ser roteado ao se fazer uma desativação relativamente global da transmissão PUT / POST em duas partes System.Net.ServicePointManager.Expect100Continue.

No entanto, isso não resolve o problema subjacente completo - a pilha ainda pode estar usando coisas específicas do HTTP 1.1, como KeepAlives etc. (embora em muitos casos as outras respostas abranjam os casos principais).

O problema real é, no entanto, que o código gerado automaticamente assume que não há problema em usar cegamente os recursos do HTTP 1.1, pois todos entendem isso. Para interromper essa suposição para um proxy de serviço da Web específico, é possível alterar a substituição do padrão subjacente HttpWebRequest.ProtocolVersiondo padrão de 1.1 , criando uma classe de proxy derivada que substitui como mostrado nesta postagem : -protected override WebRequest GetWebRequest(Uri uri)

public class MyNotAssumingHttp11ProxiesAndServersProxy : MyWS
{
    protected override WebRequest GetWebRequest(Uri uri)
    {
      HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(uri);
      request.ProtocolVersion = HttpVersion.Version10;
      return request;
    }
}

(onde MyWSestá o proxy que o assistente Adicionar Referência da Web cuspiu em você.)


UPDATE: Aqui está um impl estou usando na produção:

class ProxyFriendlyXXXWs : BasicHttpBinding_IXXX
{
    public ProxyFriendlyXXXWs( Uri destination )
    {
        Url = destination.ToString();
        this.IfProxiedUrlAddProxyOverriddenWithDefaultCredentials();
    }

    // Make it squirm through proxies that don't understand (or are misconfigured) to only understand HTTP 1.0 without yielding HTTP 417s
    protected override WebRequest GetWebRequest( Uri uri )
    {
        var request = (HttpWebRequest)base.GetWebRequest( uri );
        request.ProtocolVersion = HttpVersion.Version10;
        return request;
    }
}

static class SoapHttpClientProtocolRealWorldProxyTraversalExtensions
{
    // OOTB, .NET 1-4 do not submit credentials to proxies.
    // This avoids having to document how to 'just override a setting on your default proxy in your app.config' (or machine.config!)
    public static void IfProxiedUrlAddProxyOverriddenWithDefaultCredentials( this SoapHttpClientProtocol that )
    {
        Uri destination = new Uri( that.Url );
        Uri proxiedAddress = WebRequest.DefaultWebProxy.GetProxy( destination );
        if ( !destination.Equals( proxiedAddress ) )
            that.Proxy = new WebProxy( proxiedAddress ) { UseDefaultCredentials = true };
    }
}
Ruben Bartelink
fonte
2
Sei que você postou isso há um bom tempo, mas Ruben, você salva vidas. Esse problema me deixou louco até que me deparei com sua solução e ela funciona perfeitamente. Felicidades!
Christopher McAtackney
1
@ChrisMcAtackney De nada. Definitivamente, pareceu valer a pena o esforço de documentá-lo na época deles ... o problema geral estabelecido em torno de se tornar um problema de prioridade máxima e me incomodou até que eu o investigasse adequadamente - ainda não parecia haver nenhum suco do google para esse lado da questão. e foi um dos posts do camarada Attwood do passado que me levou a fazê-lo. (Um voto positivo com um comentário como este é fantástico)
Ruben Bartelink 12/12/12
1
Adição e exemplos maravilhosos. Obrigado!
Fadi Chamieh
5

O formulário que você está tentando emular possui dois campos, nome de usuário e senha?

Se sim, esta linha:

 postData.Add("username", "password");

não está correto.

você precisaria de duas linhas como:

 postData.Add("username", "Moose");
postData.Add("password", "NotMoosespasswordreally");

Editar:

Ok, como esse não é o problema, uma maneira de resolver isso é usar algo como o Fiddler ou o Wireshark para observar o que está sendo enviado para o servidor da Web a partir do navegador com êxito e comparar com o que está sendo enviado pelo seu código. Se você estiver indo para uma porta 80 normal do .Net, o Fiddler ainda capturará esse tráfego.

Provavelmente, existe algum outro campo oculto no formulário que o servidor da Web espera que você não esteja enviando.

alce
fonte
3

Solução do lado do proxy, enfrentei alguns problemas no processo de handshake SSL e tive que forçar meu servidor proxy a enviar solicitações usando HTTP / 1.0 para resolver o problema, definindo esse argumento no httpd.conf SetEnv force-proxy-request-1.0 1 SetEnv proxy-nokeepalive 1depois que enfrentei o erro 417 como meu aplicativo clientes estava usando HTTP / 1.1 e o proxy foi forçado a usar HTTP / 1.0, o problema foi resolvido definindo esse parâmetro no httpd.conf no lado proxy RequestHeader unset Expect earlysem a necessidade de alterar nada no lado cliente, espero que isso ajude .

Hytham
fonte
2

Para o PowerShell, é

[System.Net.ServicePointManager]::Expect100Continue = $false
TNT
fonte
1

Se você estiver usando " HttpClient " e não quiser usar a configuração global para afetar todos os programas que você pode usar:

 HttpClientHandler httpClientHandler = new HttpClientHandler();
 httpClient.DefaultRequestHeaders.ExpectContinue = false;

Se você estiver usando o " WebClient ", acho que você pode tentar remover esse cabeçalho chamando:

 var client = new WebClient();
 client.Headers.Remove(HttpRequestHeader.Expect);
Aviram Fireberger
fonte
0

Na minha situação, esse erro parece ocorrer apenas se o computador do meu cliente tiver uma política rígida de firewall, o que impede que meu programa se comunique com o serviço da web.

Portanto, a única solução que pude encontrar é detectar o erro e informar o usuário sobre a alteração manual das configurações do firewall.

Emre Can Serteli
fonte
-1

A abordagem web.config funciona para chamadas de serviços de formulário do InfoPath para regras habilitadas para serviços da web do IntApp.

  <system.net>
    <defaultProxy />
    <settings> <!-- 20130323 bchauvin -->
        <servicePointManager expect100Continue="false" />
    </settings>
  </system.net>
BobC
fonte
2
dup de stackoverflow.com/a/7358457/11635 comentário Por favor, não se você quiser adicionar um ponto
Ruben Bartelink