Http 从C#移植到F时System.Net.WebException#
我正在尝试将一些C代码移植到F C#代码取自此处(稍微向后剥离):Http 从C#移植到F时System.Net.WebException#,http,f#,httpwebresponse,c#-to-f#,Http,F#,Httpwebresponse,C# To F#,我正在尝试将一些C代码移植到F C#代码取自此处(稍微向后剥离): public bool登录(字符串p12CertificateLocation、字符串p12CertificatePassword、字符串用户名、字符串密码) { var appKey=“appKey”; string postData=string.Format(“用户名={0}&密码={1}”,用户名,密码); X509Certificate2 x509certificate=新的X509Certificate2(P12证书
public bool登录(字符串p12CertificateLocation、字符串p12CertificatePassword、字符串用户名、字符串密码)
{
var appKey=“appKey”;
string postData=string.Format(“用户名={0}&密码={1}”,用户名,密码);
X509Certificate2 x509certificate=新的X509Certificate2(P12证书位置,P12证书密码);
HttpWebRequest请求=(HttpWebRequest)WebRequest.Create(“https://identitysso.betfair.com/api/certlogin");
request.UseDefaultCredentials=true;
request.Method=“POST”;
request.ContentType=“application/x-www-form-urlencoded”;
request.Headers.Add(“X-Application”,appKey);
request.ClientCertificates.Add(x509certificate);
request.Accept=“*/*”;
使用(Stream=request.GetRequestStream())
使用(StreamWriter=newstreamwriter(stream,Encoding.Default))
writer.Write(postData);
使用(Stream Stream=((HttpWebResponse)request.GetResponse()).GetResponseStream())
使用(StreamReader=newstreamreader(stream,Encoding.Default))
上面的C#代码工作得很好。但是,当尝试运行(我认为是)F#等效代码时,在没有任何实际修改的情况下,我会收到一条错误消息
代码在同一台计算机上运行,与VS安装相同,并且具有完全相同的4个参数
我收到的错误消息位于倒数第二行:
member x.Login(username, password,p12CertificateLocation:string, p12CertificatePassword:string) =
let AppKey = "APPKEY"
let url = "https://identitysso.betfair.com/api/certlogin"
let postData = "username=" + username + "&password=" + password
let x509certificate = new X509Certificate2(p12CertificateLocation, p12CertificatePassword)
let req = HttpWebRequest.Create(url) :?> HttpWebRequest
req.ClientCertificates.Add(x509certificate)|>ignore
req.UseDefaultCredentials <- true
req.Method <- "POST"
req.ContentType <- "application/x-www-form-urlencoded"
req.Headers.Add("X-Application",AppKey)
req.Accept <-"*/*"
use stream = req.GetRequestStream()
use writer =new StreamWriter(stream,Encoding.Default)
writer.Write(postData)
// fails on this line:
use stream = (req.GetResponse() :?> HttpWebResponse ).GetResponseStream()
// with System.Net.WebException: 'The remote server returned an error: (400) Bad Request.'
use reader = new StreamReader(stream,Encoding.Default)
member x.Login(用户名、密码、p12CertificateLocation:string、p12CertificatePassword:string)=
让AppKey=“AppKey”
让url=”https://identitysso.betfair.com/api/certlogin"
让postData=“username=“+username+”&password=“+password
设x509certificate=新的X509Certificate2(p12CertificateLocation,p12CertificatePassword)
让req=HttpWebRequest.Create(url):?>HttpWebRequest
请求客户端证书。添加(x509certificate)|>忽略
此C代码中的req.UseDefaultCredentials:
使用(streamstream1=request.GetRequestStream())
使用(StreamWriter=newstreamwriter(stream1,Encoding.Default))
writer.Write(postData);
使用(stream2=((HttpWebResponse)request.GetResponse()).GetResponseStream())
使用(StreamReader=newstreamreader(stream2,Encoding.Default))
writer
和stream1
会在writer.Write
调用完成后立即刷新并关闭,然后再调用request.GetResponse()
(这一事实由于代码的格式非常有趣而有些模糊。)
在此F#代码中:
writer
和stream1
在调用req.GetResponse()
时保持活动状态并保持未刷新和未关闭状态;您需要将它们放在一个人工作用域中,以获得与C#相同的行为:
在此C#代码中:
使用(streamstream1=request.GetRequestStream())
使用(StreamWriter=newstreamwriter(stream1,Encoding.Default))
writer.Write(postData);
使用(stream2=((HttpWebResponse)request.GetResponse()).GetResponseStream())
使用(StreamReader=newstreamreader(stream2,Encoding.Default))
writer
和stream1
会在writer.Write
调用完成后立即刷新并关闭,然后再调用request.GetResponse()
(这一事实由于代码的格式非常有趣而有些模糊。)
在此F#代码中:
writer
和stream1
在调用req.GetResponse()
时保持活动状态并保持未刷新和未关闭状态;您需要将它们放在一个人工作用域中,以获得与C#相同的行为:
这不是进行HTTP POST调用的“C#方式”。在所有受支持的.NET版本(ie 4.5.2及更高版本)中,典型的方式是使用HttpClient。即使使用HttpWebRequest,也有太多冗余或矛盾的调用,例如使用默认凭据(即Windows身份验证)
C#方法是:
var-client=新的HttpClient(“https://identitysso.betfair.com/api");
var值=新字典
{
{“用户名”,用户名},
{“密码”,密码}
};
var内容=新的FormUrlEncodedContent(值);
content.Headers.Add(“X-Application”,apiKey);
var response=wait client.PostAsync(“certlogin”,content);
var responseString=await response.Content.ReadAsStringAsync();
要使用客户端证书,必须使用自定义HTTP处理程序创建客户端实例:
var handler=new WebRequestHandler();
var x509certificate=新的X509Certificate2(certPath,certPassword);
handler.ClientCertificates.Add(证书);
var client=新的HttpClient(处理程序)
{
BaseAddress=新Uri(“https://identitysso.betfair.com/api")
}
用F#编写相同的代码很简单:
let login username password (certPath:string) (certPassword:string) (apiKey:string) =
let handler = new WebRequestHandler()
let certificate = new X509Certificate2(certPath, certPassword)
handler.ClientCertificates.Add certificate |> ignore
let client = new HttpClient(handler,BaseAddress = Uri("https://identitysso.betfair.com"))
async {
let values = dict["username", username ; "password", password ]
let content = new FormUrlEncodedContent(values)
content.Headers.Add( "X-Application" ,apiKey)
let! response = client.PostAsync("api/certlogin",content) |> Async.AwaitTask
response.EnsureSuccessStatusCode() |> ignore
let! responseString = response.Content.ReadAsStringAsync() |> Async.AwaitTask
return responseString
}
客户端、处理程序是线程安全的,可以重复使用,以便存储在字段中。重复使用同一个客户端意味着操作系统不必每次都创建新的TCP/IP连接,从而提高性能。最好单独创建客户端:
let buildClient (certPath:string) (certPassword:string) =
let handler = new WebRequestHandler()
let certificate = new X509Certificate2(certPath, certPassword)
handler.ClientCertificates.Add certificate |> ignore
new HttpClient(handler,BaseAddress = Uri("https://identitysso.betfair.com"))
let login (client:HttpClient) username password (apiKey:string) =
async {
let values = dict["username", username ; "password", password ]
let content = new FormUrlEncodedContent(values)
content.Headers.Add( "X-Application" ,apiKey)
let! response = client.PostAsync("api/certlogin",content) |> Async.AwaitTask
response.EnsureSuccessStatusCode() |> ignore
let! responseString = response.Content.ReadAsStringAsync() |> Async.AwaitTask
//Do whatever is needed here
return responseString
}
这不是进行HTTP POST调用的“C#方式”。在所有受支持的.NET版本(ie 4.5.2及更高版本)中,典型的方式是使用HttpClient。即使使用HttpWebRequest,也有太多冗余或矛盾的调用,例如使用默认凭据(即Windows身份验证)
C#方法是:
var-client=新的HttpClient(“https://identitysso.betfair.com/api");
var值=新字典
{
{“用户名”,用户名},
{“密码”,密码}
};
var内容=新的FormUrlEncodedContent(值);
content.Headers.Add(“X-Application”,apiKey);
var response=wait client.PostAsync(“certlogin”,content);
风险反应
let login username password (certPath:string) (certPassword:string) (apiKey:string) =
let handler = new WebRequestHandler()
let certificate = new X509Certificate2(certPath, certPassword)
handler.ClientCertificates.Add certificate |> ignore
let client = new HttpClient(handler,BaseAddress = Uri("https://identitysso.betfair.com"))
async {
let values = dict["username", username ; "password", password ]
let content = new FormUrlEncodedContent(values)
content.Headers.Add( "X-Application" ,apiKey)
let! response = client.PostAsync("api/certlogin",content) |> Async.AwaitTask
response.EnsureSuccessStatusCode() |> ignore
let! responseString = response.Content.ReadAsStringAsync() |> Async.AwaitTask
return responseString
}
let buildClient (certPath:string) (certPassword:string) =
let handler = new WebRequestHandler()
let certificate = new X509Certificate2(certPath, certPassword)
handler.ClientCertificates.Add certificate |> ignore
new HttpClient(handler,BaseAddress = Uri("https://identitysso.betfair.com"))
let login (client:HttpClient) username password (apiKey:string) =
async {
let values = dict["username", username ; "password", password ]
let content = new FormUrlEncodedContent(values)
content.Headers.Add( "X-Application" ,apiKey)
let! response = client.PostAsync("api/certlogin",content) |> Async.AwaitTask
response.EnsureSuccessStatusCode() |> ignore
let! responseString = response.Content.ReadAsStringAsync() |> Async.AwaitTask
//Do whatever is needed here
return responseString
}