最近一段时间做了一个项目,需要将生成的soap请求的xml字符串提交给服务端地址,然后将返回的soap响应进行分析判断得到结果。这其中需要在请求的同时附上证书进行验证。
其实这个功能实现起来不是很难,但是在研究的过程中有几个小问题却让我多费了好几个脑细胞,在此记录下,为自己和博友们做一下小小的提醒。
其中主要出现的问题有以下几点:
- 报WebException异常: The request was aborted: Could not create SSL/TLS secure channel.
- 报服务器内部500错误
这两个问题主要在代码中哪些部分会出现呢,就去代码中看看。
OK,先上段代码
1 /// <summary> 2 /// 进行进行ssl验证的服务器进行soap请求 3 /// </summary> 4 /// <param name="tokenId">需要生成soap请求的参数1</param> 5 /// <param name="secureCode">需要生成soap请求的参数2</param> 6 /// <returns>soap请求的响应内容</returns> 7 public string SoapRequest(string tokenId, string secureCode) 8 { 9 string url = "https://[The request url for your soap request]"; 10 string soapAction = "[the action name]"; 11 12 // GenerateSoapContent 方法用来构造Soap的XML请求 13 string soapRequest = GenerateSoapContent(tokenId, secureCode); 14 15 // 这句话用来调用ValidateFbCertificate方法,让远程ssl证书验证设为成功 16 ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateFbCertificate); 17 18 // 设置安全协议类型,这个需要对应具体需求 19 ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3; 20 21 HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url); 22 webRequest.Headers.Add("SOAPAction", soapAction); 23 webRequest.ContentLength = soapRequest.Length; 24 webRequest.Method = "POST"; 25 webRequest.ProtocolVersion = HttpVersion.Version10; 26 27 // 将证书加入web请求中 28 X509Certificate2 cert2 = new X509Certificate2(Server.MapPath("pfx或者p12格式的文件名"), "证书密码", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet); 29 webRequest.ClientCertificates.Add(cert2); 30 31 using (Stream requestStream = webRequest.GetRequestStream()) 32 { 33 byte[] paramBytes = Encoding.UTF8.GetBytes(soapRequest); 34 requestStream.Write(paramBytes, 0, paramBytes.Length); 35 requestStream.Close(); 36 } 37 38 WebResponse webResponse = null; 39 try 40 { 41 webResponse = webRequest.GetResponse(); 42 using (StreamReader myReader = new StreamReader(webResponse.GetResponseStream(), Encoding.UTF8)) 43 { 44 string response = myReader.ReadToEnd(); 45 myReader.Close(); 46 return response; 47 } 48 } 49 catch (Exception ex) 50 { 51 throw ex; 52 } 53 } 54 55 /// <summary> 56 /// Generate the soap request 57 /// </summary> 58 /// <param name="userId">the token Id</param> 59 /// <param name="secureCode">the secure code</param> 60 /// <returns>the request string</returns> 61 string GenerateSoapContent(string userId, string secureCode) 62 { 63 return "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:vip=\"https://schemas.symantec.com/vip/2011/04/vipuserservices\">" 64 + "<soapenv:Header/>" 65 + "<soapenv:Body>" 66 + "<vip:AuthenticateUserRequest>" 67 + "<vip:requestId>1323</vip:requestId>" 68 + "<vip:userId>" + userId + "</vip:userId>" 69 + "<vip:otpAuthData>" 70 + "<vip:otp>" + secureCode + "</vip:otp>" 71 + "</vip:otpAuthData>" 72 + "</vip:AuthenticateUserRequest>" 73 + "</soapenv:Body>" 74 + "</soapenv:Envelope>"; 75 }
以上是基本已经将问题解决的代码。
在代码中,当16,19行代码未写,可能就会出现异常: “The request was aborted: Could not create SSL/TLS secure channel.”。当28,29行未写,那肯定会出现该异常。
在出现500内部错误时,我一直以为是服务器端的方法出现了错误,直到用php的curl执行soap请求成功返回结果,才发现是一个小小的问题导致了这个“严重”的麻烦。具体原因是因为在写soap请求文本时,原来的GenerateSoapContent方法是如下写法:
1 /// <summary> 2 /// Generate the soap request 3 /// </summary> 4 /// <param name="userId">the token Id</param> 5 /// <param name="secureCode">the secure code</param> 6 /// <returns>the request string</returns> 7 string GenerateSoapContent(string userId, string secureCode) 8 { 9 StringBuilder builder = new StringBuilder(); 10 builder.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"); 11 builder.AppendLine("<S:Envelope xmlns:S=\"http://schemas.xmlsoap.org/soap/envelope/\" >"); 12 builder.AppendLine(" <S:Body>"); 13 builder.AppendLine(" <AuthenticateUserRequest xmlns=\"https://schemas.vip.symantec.com\">"); 14 builder.AppendLine(" <requestId>" + ((Int32)(new Random().NextDouble() * 1000) + 1) + "</requestId>"); 15 builder.AppendLine(" <userId>" + userId + "</userId>"); 16 builder.AppendLine(" <pin></pin>"); 17 builder.AppendLine(" <otpAuthData>"); 18 builder.AppendLine(" <otp>" + secureCode + "</otp>"); 19 builder.AppendLine(" </otpAuthData>"); 20 builder.AppendLine(" </AuthenticateUserRequest>"); 21 builder.AppendLine(" </S:Body>"); 22 builder.AppendLine("</S:Envelope>"); 23 return builder.ToString(); 24 }
看出与最上面的方法有什么不同了吗?是的,StringBuilder的AppendLine害了我,因为这样会在最终生成的soap字符串中添加了“\r\n"字符,那传到服务器端就不会被服务器端方法识别为soap的xml格式,就会抛出异常。
其实这些都不是什么难以解决的问题,主要是因为自己不注意。在此留下这篇文章,为后来人提供些许帮助。
时间: 2024-10-05 10:06:37