当我尝试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没有区别。

是什么导致了这个异常?


当前回答

检查网络连接是否重定向。

我有这个问题时,在错误的wifi和任何网络请求重定向到一个公司登录页面。

其他回答

检查网络连接是否重定向。

我有这个问题时,在错误的wifi和任何网络请求重定向到一个公司登录页面。

另一种方式——

将这些行添加到应用程序配置文件配置部分:

<system.net>
    <settings>
        <servicePointManager expect100Continue="false" />
    </settings>
</system.net>

在我的情况下,这个错误似乎只有在我客户的计算机有严格的防火墙策略时才会发生,防火墙策略阻止了我的程序与web服务通信。

所以我能找到的唯一解决方案是捕捉错误并通知用户手动更改防火墙设置。

您试图模拟的表单是否有两个字段,用户名和密码?

如果是这样,这一行:

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

不正确。

你需要两行代码:

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

编辑:

好吧,既然这不是问题所在,解决这个问题的一种方法是使用Fiddler或Wireshark之类的工具来查看从浏览器成功发送到web服务器的内容,然后将其与从您的代码发送的内容进行比较。如果从。net转到普通的80端口,Fiddler仍然会捕获这些流量。

表单上可能有一些web服务器期望你没有发送的其他隐藏字段。

缺省向导生成的SOAP Web服务代理也会出现同样的情况和错误(如果WCF系统上也出现这种情况,则不是100%。ServiceModel stack)在运行时:

最终用户机器(在Internet设置中)被配置为使用不理解HTTP 1.1的代理 客户端最终发送了一些HTTP 1.0代理不理解的东西(通常是HTTP POST或PUT请求的一部分,因为标准协议约定将请求分为两部分发送,如备注中所述)

... 结果是417。

正如在其他答案中所述,如果您遇到的特定问题是Expect报头导致了问题,那么可以通过通过System.Net.ServicePointManager.Expect100Continue相对全局地关闭由两部分组成的PUT/POST传输来绕过该特定问题。

然而,这并不能完全解决底层问题——堆栈可能仍然使用HTTP 1.1特定的东西,如KeepAlives等(尽管在许多情况下,其他答案确实涵盖了主要情况)。

然而,实际的问题是自动生成的代码假设盲目使用HTTP 1.1设施是可以的,因为每个人都明白这一点。要停止对特定Web服务代理的这种假设,可以更改覆盖默认的底层HttpWebRequest。通过创建一个派生的代理类来覆盖受保护的覆盖WebRequest GetWebRequest(Uri Uri),从1.1的默认协议版本

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

(其中MyWS是添加Web参考向导向您发出的代理。)


更新:这是我在生产中使用的impl:

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 };
    }
}