我的客户需要TLS 1.2才能符合PCI标准。 Xamarin Android不支持TLS 1.2。根据这个 和Native HttpClientHandler,您可以使用HttpClient及其特殊机制来访问Android 5及更高版本上的本机Java支持,也可以使用ModernHttpClient。
但是,使用SvcUtil生成的WCF SOAP Webservice代理似乎使用HttpWebRequest,而不是HttpClient。
使用HttpClient(或ModernHttpClient)调用WCF SOAP服务的推荐方法是什么?我是否必须手动编写自己的接口,还是可以使用代理类并自行序列化/反序列化?我不必完全从头开始,特别是因为看起来TLS 1.2目前正在添加到Mono中。
答案 0 :(得分:0)
我已经使用过这种类型的服务,它正在这里工作,我有共享相关代码,请尝试一下。
static void TryByWebRequest(string soapMethod)
{
XmlDocument soapEnvelopeXml = new XmlDocument();
soapEnvelopeXml.LoadXml(@"
<s:Envelope xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/"">
<s:Body>
<" + soapMethod + @"
xmlns=""your URI""
xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"">
<InputXMLString>
" +
System.Security.SecurityElement.Escape(inputXML)
+ @"
</InputXMLString>
<OutputXMLString/>
</" + soapMethod + @">
</s:Body>
</s:Envelope>");
using (Stream stream = request.GetRequestStream())
{
soapEnvelopeXml.Save(stream);
}
using (WebResponse response = request.GetResponse())
{
using (StreamReader rd = new StreamReader(response.GetResponseStream()))
{
string soapResult = rd.ReadToEnd();
Console.WriteLine(soapResult);
}
}
}
static HttpWebRequest CreateWebRequest(string soapMethod)
{
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(@"Your .asmx URL ");
webRequest.Headers.Add(@"SOAPAction", "your URI \" + soapMethod);
webRequest.ContentType = "text/xml;charset=\"utf-8\"";
webRequest.Accept = "text/xml";
webRequest.Method = "POST";
return webRequest;
}
答案 1 :(得分:0)
我得到了这个工作。由于这是一个(希望)临时解决方案,我的目标是为代理生成的类创建插件替换,我非常接近。关键是弄清楚如何使用DataContractSerializer创建一个SOAP信封来发送和反序列化结果。
除了发送到Web服务的SOAP信封的序列化之外,我成功了。我最终手动将XML包装在<envelope>
和<body>
标记中。我做的任何事情都不能让DataContractSerializer正确地创建它们,尽管Body内容没问题。 Deserializer能够毫无问题地处理来自Web服务的响应。 WCF服务对于SOAP信封的格式非常挑剔,并且正确地注释类是一个挑战。
对于每个函数调用,我必须创建一个Request对象,它包装发送给Web服务的参数,以及一个包装out参数和返回代码的Response对象。
这些看起来像这样,其中FunctionName是代理生成的WCF函数调用的名称。
// request envelope
[System.Runtime.Serialization.DataContractAttribute(Name = "FunctionName", Namespace = "http://tempuri.org/")]
public class FunctionName_Request
{
[System.Runtime.Serialization.DataMemberAttribute()]
public NameSpaceFunctionNameObject1 CallingObject1;
[System.Runtime.Serialization.DataMemberAttribute()]
public NameSpaceFunctionNameObject2 CallingObject2;
}
// response envelope
[System.Runtime.Serialization.DataContractAttribute(Name = "Envelope", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class FunctionName_ResponseEnvelope
{
[System.Runtime.Serialization.DataContractAttribute(Name = "Body", Namespace = "http://tempuri.org/")]
public class FunctionName_ResponseBody
{
[System.Runtime.Serialization.DataContractAttribute(Name = "FunctionNameResponse", Namespace = "http://tempuri.org/")]
public class FunctionName_Response
{
[System.Runtime.Serialization.DataMemberAttribute()]
public FunctionNameReturnCodes Result;
[System.Runtime.Serialization.DataMemberAttribute()]
public FunctionNameResponseObject Response;
}
[System.Runtime.Serialization.DataMemberAttribute()]
public FunctionName_Response FunctionNameResponse;
}
[System.Runtime.Serialization.DataMemberAttribute()]
public FunctionName_ResponseBody Body;
}
然后,我可以编写一个我的客户端代码可以调用的替换函数,它与原始代理生成的函数具有完全相同的签名。
// FunctionName
public FunctionNameReturnCodes FunctionName(out FunctionNameResponseObject Response, NameSpaceFunctionNameObject1 CallingObject1, NameSpaceFunctionNameObject2 CallingObject2)
{
// create the request envelope
FunctionName_Request req = new FunctionName_Request();
req.CallingObject1 = CallingObject1;
req.CallingObject2 = CallingObject2;
// make the call
FunctionName_ResponseEnvelope resp = MakeWCFCall<FunctionName_ResponseEnvelope>(_EndpointAddress, _ServerName, req);
// get the response object
Response = resp.Body.FunctionName_Response.Response;
// result
return resp.Body.FunctionName_Response.Result;
}
最后,这是将对象实际序列化和反序列化为HttpClient的函数。在我的例子中,这些是同步的,但您可以轻松地将其调整为在标准异步情况下工作。它的模板,因此它适用于任何代理生成的类型。
/////////////////////////////////////////////////////////////////////
// make a WCF call using an HttpClient object
// uses the DataContractSerializer to serialze/deserialze the messages from the objects
//
// We manually add the <s:Envelope> and <s:Body> tags. There should be a way to get
// the DataContractSerializer to write these too, but everything I tried gave a message
// that was not able to be procesed by the service. This includes the Message object.
// Deserializing works fine, but serializing did not.
private T MakeWCFCall<T>(string strEndpoint, string strServerName, object SourceObject)
{
T Response = default(T);
string strSoapMessage = "";
string strSoapAction = "";
// get the Soap Action by using the DataContractAttribute's name
// start by getting the list of custom attributes.
// there should only be the one
object[] oaAttr = SourceObject.GetType().GetCustomAttributes(false);
if (oaAttr.Length > 0)
{
// iterate just in case there are more
foreach (DataContractAttribute oAttr in oaAttr)
{
// make sure we got one
if (oAttr != null)
{
// this is the action!
strSoapAction = oAttr.Name;
break;
}
}
}
// serialize the request into a string
// use a memory stream as the source
using (MemoryStream ms = new MemoryStream())
{
// create the DataContractSerializer
DataContractSerializer ser = new DataContractSerializer(SourceObject.GetType());
// serialize the object into the memory stream
ser.WriteObject(ms, SourceObject);
// seek to the beginning so we can read back out of the stream
ms.Seek(0, SeekOrigin.Begin);
// create the stream reader
using (var streamReader = new StreamReader(ms))
{
// read the message back out, adding the Envelope and Body wrapper
strSoapMessage = @"<s:Envelope xmlns:s = ""http://schemas.xmlsoap.org/soap/envelope/""><s:Body>" + streamReader.ReadToEnd() + @"</s:Body></s:Envelope>";
}
}
// now create the HttpClient connection
using (var client = new HttpClient(new NativeMessageHandler()))
{
//specify to use TLS 1.2 as default connection
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
// add the Soap Action header
client.DefaultRequestHeaders.Add("SOAPAction", "http://tempuri.org/" + strServerName + "/" + strSoapAction);
// encode the saop message
var content = new StringContent(strSoapMessage, Encoding.UTF8, "text/xml");
// post to the server
using (var response = client.PostAsync(new Uri(strEndpoint), content).Result)
{
// get the response back
var soapResponse = response.Content.ReadAsStringAsync().Result;
// create a MemoryStream to use for serialization
using (MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(soapResponse)))
{
// create the reader
// set the quotas
XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(
memoryStream,
Encoding.UTF8,
new XmlDictionaryReaderQuotas() { MaxArrayLength = 5000000, MaxBytesPerRead = 5000000, MaxStringContentLength = 5000000 },
null);
// create the Data Contract Serializer
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
// deserialize the response
Response = (T)serializer.ReadObject(reader);
}
}
}
// return the response
return Response;
}
这种方法允许我快速为我的所有WCF服务功能编写包装器,并且到目前为止它运行良好。