HTTP POST zwraca błąd: 417 „Oczekiwanie nie powiodło się”.

212

Gdy próbuję wysłać POST do adresu URL, powoduje to następujący wyjątek:

Serwer zdalny zwrócił błąd: (417) Oczekiwanie nie powiodło się.

Oto przykładowy kod:

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.

Korzystanie z HttpWebRequest/HttpWebResponsepary lub an HttpClientnie robi różnicy.

Co powoduje ten wyjątek?

Saeb Amini
źródło
2
Wydaje się, że problem występuje, gdy aplikacja komunikuje się za pośrednictwem serwera proxy. Napisana przeze mnie aplikacja .NET działała, gdy była bezpośrednio połączona z Internetem, ale nie działała, gdy znajdowała się za serwerem proxy.
Salman A
2
Zaobserwowano ten warunek, gdy klient działa przez serwer proxy HTTP 1.0 (tylko). Klient (serwer proxy asmx bez konfiguracji) wysyła żądanie HTTP 1.1, a serwer proxy (zanim jakikolwiek serwer zdoła się zaangażować) następnie odrzuca to, co serwer proxy wysyła. Gdyby użytkownik końcowy ma ten problem, stosując rozwiązanie config poniżej jest odpowiedni obejście gdyż powodują wnioski mają być generowane bez polegania na pełnomocnika Zrozumienie Expectnagłówek który domyślnie zostanie dodany jak Expect100Continuejest truedomyślnie.
Ruben Bartelink

Odpowiedzi:

478

System.Net.HttpWebRequest dodaje nagłówek „Nagłówek HTTP„ Oczekuj: 100-Kontynuuj ”” do każdego żądania, chyba że wyraźnie poprosisz go, aby nie ustawiał tej właściwości statycznej na false:

System.Net.ServicePointManager.Expect100Continue = false;

Niektóre serwery dławią się tym nagłówkiem i odsyłają wyświetlony błąd 417.

Daj temu szansę.

xcud
źródło
5
Miałem jakiś kod, który mówił do Twittera, który nagle przestał działać dzień po Bożym Narodzeniu. Zrobili aktualizację lub zmianę konfiguracji, która spowodowała, że ​​ich serwery zaczęły dusić się w tym nagłówku. Znalezienie rozwiązania było uciążliwe.
xcud
8
Myślę, że zdobyłem dodatkowe 10 punktów za zaakceptowanie odpowiedzi w wątku, w którym Jon Skeet zamieścił rozwiązanie.
xcud
1
Dzięki! Należy to zauważyć gdzieś w przykładach msdn, takich jak msdn.microsoft.com/en-us/library/debx8sh9.aspx
Eugene
25
Możesz także określić tę właściwość w pliku app.config: <system.net> <ustawienia> <servicePointManager expect100Continue = "false" />. nahidulkibria.blogspot.com/2009/06/…
Andre Luus
2
Uwaga: to ustawienie dotyczy również ServiceReferences i zakładam WebReferences.
Myster
114

Inny sposób -

Dodaj następujące wiersze do sekcji konfiguracji pliku konfiguracyjnego aplikacji:

<system.net>
    <settings>
        <servicePointManager expect100Continue="false" />
    </settings>
</system.net>
Engin Ardıç
źródło
6
Działa bardzo dobrze, gdy musisz dokonać zmiany operacyjnej i nie jesteś gotowy na zmianę kodu.
dawebber
1
Co robi ta linia konfiguracji?
J86
31

Ta sama sytuacja i błąd mogą również wystąpić w przypadku domyślnego generowanego przez kreatora proxy usługi SOAP Web Service (nie w 100%, jeśli dotyczy to również System.ServiceModelstosu WCF ) w czasie wykonywania:

  • komputer użytkownika końcowego jest skonfigurowany (w Ustawieniach internetowych) do używania serwera proxy, który nie rozumie protokołu HTTP 1.1
  • klient ostatecznie wysyła coś, czego serwer proxy HTTP 1.0 nie rozumie (zwykle Expectnagłówek jako część HTTP POSTlub PUTżądania ze względu na standardową konwencję protokołu wysyłania żądania w dwóch częściach, jak opisano w uwagach tutaj )

... co daje 417.

Jak opisano w innych odpowiedziach, jeśli konkretnym problemem, na który Expectnatrafisz, jest to, że przyczyną problemu jest nagłówek, to ten konkretny problem można rozwiązać, wykonując względnie globalne wyłączenie dwuczęściowej transmisji PUT / POST przez System.Net.ServicePointManager.Expect100Continue.

Nie rozwiązuje to jednak całego podstawowego problemu - stos może nadal używać specyficznych dla HTTP 1.1 rzeczy, takich jak KeepAlives itp. (Choć w wielu przypadkach inne odpowiedzi obejmują główne przypadki).

Faktyczny problem polega jednak na tym, że automatycznie generowany kod zakłada, że ​​można ślepo korzystać z urządzeń HTTP 1.1, ponieważ wszyscy to rozumieją. Aby zatrzymać to założenie dla konkretnego serwera proxy usługi sieci Web, można zmienić zastąpienie domyślnego elementu bazowego HttpWebRequest.ProtocolVersionod domyślnego 1.1 , tworząc pochodną klasę proxy, która zastępuje, jak pokazano w tym poście : -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;
    }
}

(gdzie MyWSjest serwer proxy, na który wypluł cię kreator dodawania odnośników internetowych).


AKTUALIZACJA: Oto implik, którego używam w produkcji:

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
źródło
2
Wiem, że napisałeś to jakiś czas temu, ale Ruben, jesteś ratownikiem. Ten problem doprowadzał mnie do szału, dopóki nie natknąłem się na twoje rozwiązanie i działa idealnie. Twoje zdrowie!
Christopher McAtackney,
1
@ChrisMcAtackney Nie ma za co. Zdecydowanie wydawało się to warte wysiłku dokumentowania go w tamtym czasie ... ogólny problem na marginesie stania się sprawą o najwyższym priorytecie i po prostu przeszkadzał mi, dopóki nie zbadałem go właściwie - ale nie wydawał się być sokiem z google dla ta strona problemu. i to był jeden z postów tego faceta z Attwood z drogi, który zmusił mnie do tego. (Głos z takim komentarzem jest fantastyczny BTW)
Ruben Bartelink,
1
Cudowny dodatek i przykłady. Dzięki!
Fadi Chamieh
5

Czy formularz, który próbujesz emulować, ma dwa pola, nazwę użytkownika i hasło?

Jeśli tak, ten wiersz:

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

nie jest poprawne.

potrzebujesz dwóch linii, takich jak:

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

Edytować:

Ok, ponieważ to nie jest problem, jednym ze sposobów rozwiązania tego problemu jest użycie czegoś takiego jak Fiddler lub Wireshark, aby z powodzeniem oglądać, co jest wysyłane do serwera internetowego z przeglądarki, a następnie porównywać to z tym, co jest wysyłane z twojego kodu. Jeśli idziesz do normalnego portu 80 z .Net, Fiddler nadal będzie przechwytywał ten ruch.

Prawdopodobnie istnieje inne ukryte pole w formularzu, którego serwer internetowy oczekuje, że go nie wysyłasz.

Łoś
źródło
3

Rozwiązanie od strony serwera proxy napotkałem pewne problemy w procesie uzgadniania protokołu SSL i musiałem zmusić serwer proxy do wysyłania żądań za pomocą protokołu HTTP / 1.0 w celu rozwiązania problemu, ustawiając ten argument w httpd.conf, SetEnv force-proxy-request-1.0 1 SetEnv proxy-nokeepalive 1po czym napotkałem błąd 417 jako moja aplikacja kliencka używała protokołu HTTP / 1.1, a serwer proxy został zmuszony do korzystania z protokołu HTTP / 1.0, problem został rozwiązany przez ustawienie tego parametru w httpd.conf po stronie serwera proxy RequestHeader unset Expect earlybez potrzeby zmiany czegokolwiek po stronie klienta. Mam nadzieję, że to pomoże .

Hytham
źródło
2

W przypadku PowerShell tak jest

[System.Net.ServicePointManager]::Expect100Continue = $false
TNT
źródło
1

Jeśli używasz „ HttpClient ” i nie chcesz używać konfiguracji globalnej, aby wpływać na wszystkie programy, których możesz używać:

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

Jeśli używasz „ WebClient ”, myślę, że możesz spróbować usunąć ten nagłówek, dzwoniąc:

 var client = new WebClient();
 client.Headers.Remove(HttpRequestHeader.Expect);
Aviram Fireberger
źródło
0

W mojej sytuacji ten błąd wydaje się występować tylko wtedy, gdy komputer mojego klienta ma ścisłe zasady zapory ogniowej, co uniemożliwia mojemu programowi komunikowanie się z usługą internetową.

Jedynym rozwiązaniem, jakie mogłem znaleźć, jest złapanie błędu i poinformowanie użytkownika o ręcznej zmianie ustawień zapory.

Emre Can Serteli
źródło
-1

Podejście web.config działa w przypadku wywołań usług formularzy InfoPath do reguł włączonych usług internetowych IntApp.

  <system.net>
    <defaultProxy />
    <settings> <!-- 20130323 bchauvin -->
        <servicePointManager expect100Continue="false" />
    </settings>
  </system.net>
BobC
źródło
2
dup of stackoverflow.com/a/7358457/11635 Skomentuj, jeśli chcesz dodać punkt
Ruben Bartelink,