HTTP POST возвращает ошибку: 417 «Ожидание не выполнено».

212

Когда я пытаюсь POST к URL, это приводит к следующему исключению:

Удаленный сервер возвратил ошибку: (417) Ошибка ожидания.

Вот пример кода:

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.

Использование HttpWebRequest/HttpWebResponseпары или HttpClientне имеет значения.

Что вызывает это исключение?

Саиб Амини
источник
2
Проблема возникает, когда ваше приложение связывается через прокси-сервер. Приложение .NET, которое я написал, работало, когда оно было напрямую подключено к Интернету, но не тогда, когда оно находилось за прокси-сервером.
Салман А
2
Наблюдалось это условие, когда клиент работает через прокси-сервер HTTP 1.0 (только). Клиент (прокси-сервер asmx без какой-либо конфигурации) отправляет запрос HTTP 1.1, и прокси-сервер (до того, как какой-либо сервер сможет подключиться) затем отклоняет то, что отправляет прокси. Если у конечного пользователя есть эта проблема, использование решения конфигурации ниже является подходящим обходным путем, поскольку это заставило бы запросы генерироваться без уверенности в том, что прокси понимает Expectзаголовок, который по умолчанию добавляется как Expect100Continueесть trueпо умолчанию.
Рубен Бартелинк

Ответы:

478

System.Net.HttpWebRequest добавляет заголовок «HTTP-заголовок« Expect: 100-Continue »» к каждому запросу, если только вы явно не просите об этом, установив для этого статического свойства значение false:

System.Net.ServicePointManager.Expect100Continue = false;

Некоторые серверы заглушают этот заголовок и возвращают ошибку 417, которую вы видите.

Дайте этому шанс.

xcud
источник
5
У меня был какой-то код, который говорил с Twitter, который внезапно перестал работать на следующий день после Рождества. Они сделали обновление или изменение конфигурации, из-за чего их серверы начали задыхаться от этого заголовка. Было трудно найти решение.
xcud
8
Я думаю, что набрал дополнительные 10 баллов за принятие ответа в ветке, в которую Джон Скит опубликовал решение.
xcud
1
Спасибо! Это должно быть отмечено где-то в примерах msdn,
Евгений
25
Вы также можете указать это свойство в своем app.config: <system.net> <settings> <servicePointManager hope100Continue = "false" />. nahidulkibria.blogspot.com/2009/06/…
Андре Луус
2
Примечание: этот параметр также применяется к ServiceReferences, и я предполагаю, что WebReferences.
Myster
115

По-другому -

Добавьте эти строки в раздел конфигурации файла конфигурации вашего приложения:

<system.net>
    <settings>
        <servicePointManager expect100Continue="false" />
    </settings>
</system.net>
Энгин Ардич
источник
6
Работает очень хорошо, когда вам нужно внести оперативные изменения и не готовы к изменению кода.
Давебер
1
Что делает эта строка конфигурации?
J86
31

Та же самая ситуация и ошибка могут также возникнуть с мастером по умолчанию, сгенерированным прокси-сервером веб-службы SOAP (не 100%, если это также имеет место в WCF). System.ServiceModel стеке ), когда во время выполнения:

  • компьютер конечного пользователя настроен (в настройках Интернета) на использование прокси, который не понимает HTTP 1.1
  • клиент заканчивает тем, что отправляет что-то, чего не понимает прокси HTTP 1.0 (обычно Expectзаголовок как часть HTTP POSTили PUTзапроса из-за стандартного протокола протокола отправки запроса в двух частях, как описано в примечаниях здесь )

... получая 417.

Как описано в других ответах, если конкретная проблема, с которой вы сталкиваетесь, заключается в том, что Expectзаголовок вызывает проблему, то эту конкретную проблему можно обойти, выполнив относительно глобальное отключение двухчастной передачи PUT / POST черезSystem.Net.ServicePointManager.Expect100Continue .

Однако это не решает основную проблему - стек все еще может использовать специфичные для HTTP 1.1 вещи, такие как KeepAlives и т. Д. (Хотя во многих случаях другие ответы охватывают основные случаи).

Однако реальная проблема заключается в том, что автоматически сгенерированный код предполагает, что можно использовать вслепую, используя средства HTTP 1.1, поскольку все это понимают. Чтобы прекратить это предположение для определенного прокси-сервера веб-службы, можно изменить переопределение значения HttpWebRequest.ProtocolVersionпо умолчанию на значение по умолчанию для 1.1, путем создания производного класса Proxy, который переопределяет, как показано в этом посте : -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;
    }
}

(где MyWSнаходится прокси, над которым выпал мастер добавления веб-ссылок?)


ОБНОВЛЕНИЕ: Вот что я использую в производстве:

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 };
    }
}
Рубен Бартелинк
источник
2
Я знаю, что вы опубликовали это довольно давно, но Рубен, вы спасатель. Эта проблема сводила меня с ума, пока я не наткнулся на ваше решение, и оно работает отлично. Ура!
Кристофер Макаткни
1
@ChrisMcAtackney Не за что. Это определенно стоило усилий, чтобы документировать это в то время ... общая проблема, которая возникла на грани превращения в проблему первостепенной важности, и просто беспокоила меня, пока я не проверил ее должным образом - хотя, похоже, не было никакого сока Google для эта сторона вопроса. и это был один из постов того парня из Этвуда, который заставил меня сделать это. (Апвот с таким комментарием - это фантастика)
Рубен Бартелинк
1
Чудесное дополнение и примеры. Спасибо!
Фади Чамие
5

Имеет ли форма, которую вы пытаетесь эмулировать, два поля: имя пользователя и пароль?

Если это так, эта строка:

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

не является правильным.

вам понадобятся две строки вроде:

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

Редактировать:

Хорошо, так как это не проблема, один из способов решения этой проблемы - использовать что-то вроде Fiddler или Wireshark для успешного просмотра того, что отправляется на веб-сервер из браузера, а затем сравнить это с тем, что отправляется из вашего кода. Если вы идете на обычный порт 80 из .Net, Fiddler по-прежнему будет захватывать этот трафик.

Возможно, в форме есть еще одно скрытое поле, которое веб-сервер ожидает, что вы не отправляете.

лось
источник
3

Решение со стороны прокси, я столкнулся с некоторыми проблемами в процессе рукопожатия SSL, и мне пришлось заставить мой прокси-сервер отправлять запросы, используя HTTP / 1.0, чтобы решить эту проблему, установив этот аргумент в httpd.conf, SetEnv force-proxy-request-1.0 1 SetEnv proxy-nokeepalive 1после чего я столкнулся с ошибкой 417 как мое клиентское приложение использовало HTTP / 1.1 и прокси был вынужден использовать HTTP / 1.0, проблема была решена путем установки этого параметра в httpd.conf на стороне прокси RequestHeader unset Expect earlyбез необходимости что-либо менять на стороне клиента, надеюсь, это поможет ,

Hytham
источник
2

Для Powershell это

[System.Net.ServicePointManager]::Expect100Continue = $false
тротил
источник
1

Если вы используете « HttpClient » и не хотите использовать глобальную конфигурацию, чтобы повлиять на все ваши программы, которые вы можете использовать:

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

Если вы используете " WebClient ", я думаю, вы можете попытаться удалить этот заголовок, вызвав:

 var client = new WebClient();
 client.Headers.Remove(HttpRequestHeader.Expect);
Авирам Файербергер
источник
0

В моей ситуации эта ошибка возникает только в том случае, если на компьютере моего клиента установлена ​​строгая политика брандмауэра, которая не позволяет моей программе взаимодействовать с веб-службой.

Поэтому единственное решение, которое я смог найти, - это отловить ошибку и сообщить пользователю об изменении настроек брандмауэра вручную.

Эмре Кан Сертели
источник
-1

Подход web.config работает для вызовов служб формы InfoPath к правилам, включенным в веб-службу IntApp.

  <system.net>
    <defaultProxy />
    <settings> <!-- 20130323 bchauvin -->
        <servicePointManager expect100Continue="false" />
    </settings>
  </system.net>
BobC
источник
2
dup of stackoverflow.com/a/7358457/11635 Пожалуйста, прокомментируйте там, если вы хотите добавить точку
Рубен Бартелинк