Lorsque j'essaie de POST vers une URL, l'exception suivante est générée:
Le serveur distant a renvoyé une erreur: (417) Échec de l'attente.
Voici un exemple de code:
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.
L'utilisation d'un HttpWebRequest/HttpWebResponse
paire ou d'un HttpClient
ne fait aucune différence.
Quelle est la cause de cette exception?
System.Net.HttpWebRequest ajoute l'en-tête 'En-tête HTTP "Expect: 100-Continue"' à chaque demande, à moins que vous ne le lui demandiez explicitement en définissant cette propriété statique sur false:
System.Net.ServicePointManager.Expect100Continue = false;
Certains serveurs s'étouffent avec cet en-tête et renvoient l'erreur 417 que vous voyez.
Donnez un coup de feu.
Autrement -
Ajoutez ces lignes à la section de configuration de votre fichier de configuration d’application:
<system.net>
<settings>
<servicePointManager expect100Continue="false" />
</settings>
</system.net>
Cette même situation et erreur peut également survenir avec un proxy de service Web SOAP généré par un assistant (pas à 100% si c'est également le cas sur la pile WCF _System.ServiceModel
_) lors de l'exécution:
Expect
faisant partie d'une requête HTTP POST
ou PUT
en raison d'une convention de protocole standard d'envoi de la demande deux parties comme couvert dans les remarques ici )... donnant un 417.
Comme indiqué dans les autres réponses, si le problème spécifique que vous rencontrez est que l’en-tête Expect
est à l’origine du problème, ce problème spécifique peut être acheminé en effectuant une désactivation relativement globale du PUT/POST transmission via System.Net.ServicePointManager.Expect100Continue
.
Cependant, cela ne résout pas le problème sous-jacent complet - la pile peut toujours utiliser des éléments spécifiques à HTTP 1.1 tels que KeepAlives, etc. (bien que dans de nombreux cas, les autres réponses couvrent les cas principaux.)
Le problème, toutefois, est que le code généré automatiquement suppose qu’il est correct d’utiliser aveuglément les installations HTTP 1.1, comme tout le monde le comprend. Pour arrêter cette hypothèse pour un proxy de service Web spécifique, vous pouvez modifier le remplacement du sous-jacent par défaut HttpWebRequest.ProtocolVersion
du paramètre par défaut de 1.1 en créant une classe de proxy dérivée qui remplace protected override WebRequest GetWebRequest(Uri uri)
comme indiqué dans cet article : -
_public class MyNotAssumingHttp11ProxiesAndServersProxy : MyWS
{
protected override WebRequest GetWebRequest(Uri uri)
{
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(uri);
request.ProtocolVersion = HttpVersion.Version10;
return request;
}
}
_
(où MyWS
est le proxy que l'Assistant Ajout de référence Web a craché sur vous.)
UPDATE: Voici un impl que j'utilise en production:
_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 };
}
}
_
Le formulaire que vous essayez d'émuler comporte-t-il deux champs, nom d'utilisateur et mot de passe?
Si oui, cette ligne:
postData.Add("username", "password");
n'est pas correcte.
vous auriez besoin de deux lignes comme:
postData.Add("username", "Moose");
postData.Add("password", "NotMoosespasswordreally");
Modifier:
D'accord, puisque ce n'est pas le problème, une solution consiste à utiliser quelque chose comme Fiddler ou Wireshark pour surveiller ce qui est envoyé au serveur Web par le navigateur, puis le comparer à ce qui est envoyé à partir de votre code. Si vous allez sur un port 80 normal depuis .Net, Fiddler capturera toujours ce trafic.
Il y a probablement un autre champ caché sur le formulaire que le serveur Web s'attend à ne pas envoyer.
Solution du côté du proxy, j'ai rencontré quelques problèmes dans le processus de négociation SSL et j'ai dû forcer mon serveur proxy à envoyer des requêtes via HTTP/1.0 pour résoudre le problème en définissant cet argument dans le fichier httpd.conf SetEnv force-proxy-request-1.0 1
SetEnv proxy-nokeepalive 1
après que j’ai rencontré l’erreur 417 car mon application client utilisait HTTP/1.1 et que le proxy était forcé d’utiliser HTTP/1.0, le problème a été résolu en définissant ce paramètre dans le fichier httpd.conf du côté du proxy RequestHeader unset Expect early
sans avoir besoin de changer quoi que ce soit du côté du client, j'espère que cela vous aidera.
Pour Powershell c'est
[System.Net.ServicePointManager]::Expect100Continue = $false
Si vous utilisez "HttpClient" et que vous ne voulez pas utiliser la configuration globale pour affecter tout votre programme, vous pouvez utiliser:
HttpClientHandler httpClientHandler = new HttpClientHandler();
httpClient.DefaultRequestHeaders.ExpectContinue = false;
Si vous utilisez "WebClient", je pense que vous pouvez essayer de supprimer cet en-tête en appelant:
var client = new WebClient();
client.Headers.Remove(HttpRequestHeader.Expect);
Dans mon cas, cette erreur ne semble se produire que si l'ordinateur de mon client est doté d'une stratégie de pare-feu stricte, ce qui empêche mon programme de communiquer avec le service Web.
La seule solution que j’ai pu trouver est donc d’attraper l’erreur et d’informer l’utilisateur de la modification manuelle des paramètres du pare-feu.