C#调用WebService的三种方法


最近用到了webService的调用,因为是远程调用,并且对方的webService是用Java写的,开发过程遇到很多问题,现在把用到的方法总结一下。

1.静态调用

这是最便捷的方法,分为直接使用webService地址调用,和使用本地xml文件调用。

如下图,如果直接调用?wsdl的地址,就把地址复制黏贴到地址框里,如果本地没法连接到网址,就把?wsdl的地址用浏览器打开,保存成xml文件,拷贝到本地,再把本地xml文件的地址【类似C:\Users\admin\Desktop\XX.xml】复制到下图的地址框里,然后点击转到,会在绿色的框里显示:“在地址XX处找到1个服务”,然后修改下命名空间【可以不改】,点击确认。然后在代码的【Connected Services】文件夹下,可以找到一个【Reference.cs】的文件,这里面就有引用的webService的方法。

调用的方法很简单,在Reference.cs里找到开发文档里提到的方法,正常实例化,调用就可以,需要引用下图蓝框里定义的命名空间。

另外,如果调用时出现这样的报错:【基础连接已关闭:未能为SSL/TLS安全通道建立信任关系】,可以按下面三步解决,

(1)需要在【Reference.cs】里添加引用:

using System.Net;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;

(2)在调用的类里添加方法:

private static bool RemoteCertificateValidate(object sender, X509Certificate cert,X509Chain chain, SslPolicyErrors error)
{
System.Console.WriteLine("Warning, trust any certificate");// trust any certificate!!!
return true;//为了通过证书验证,总是返回true
}

(3)在类的构造方法调用上面方法:

ServicePointManager.ServerCertificateValidationCallback += RemoteCertificateValidate;//验证服务器证书回调自动验证

 

 2.动态调用

动态调用需要借助帮助类,这里面我已经加上了解决【SSL/TLS】问题的代码,代码如下。

(1)帮助类

using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
using System.Net;
using System.Text;
using System.Web;
using System.Web.Caching;
using System.Web.Services.Description;
using System.Xml.Serialization;

namespace TimingExportReport
{
public class WebServiceHelper
{
/// <summary>
/// 生成dll文件保存到本地
/// </summary>
/// <param name="url">WebService地址</param>
/// <param name="className">类名</param>
/// <param name="methodName">方法名</param>
/// <param name="filePath">保存dll文件的路径</param>
public static void CreateWebServiceDLL(string url, string className, string methodName, string filePath)
{
// 1. 使用 WebClient 下载 WSDL 信息。
WebClient web = new WebClient();
//Stream stream = web.OpenRead(url + "?WSDL");

CertificateTrust.SetCertificatePolicy();//证书出现问题时调用此代码//未能为 SSL/TLS 安全通道建立信任关系

Stream stream = web.OpenRead(url);
// 2. 创建和格式化 WSDL 文档。
ServiceDescription description = ServiceDescription.Read(stream);
//如果不存在就创建file文件夹
if (Directory.Exists(filePath) == false)
{
Directory.CreateDirectory(filePath);
}

if (File.Exists(filePath + className + "_" + methodName + ".dll"))
{
//判断缓存是否过期
var cachevalue = HttpRuntime.Cache.Get(className + "_" + methodName);
if (cachevalue == null)
{
//缓存过期删除dll
File.Delete(filePath + className + "_" + methodName + ".dll");
}
else
{
// 如果缓存没有过期直接返回
return;
}
}

// 3. 创建客户端代理代理类。
ServiceDescriptionImporter importer = new ServiceDescriptionImporter();
// 指定访问协议。
importer.ProtocolName = "Soap";
// 生成客户端代理。
importer.Style = ServiceDescriptionImportStyle.Client;
importer.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync;
// 添加 WSDL 文档。
importer.AddServiceDescription(description, null, null);
// 4. 使用 CodeDom 编译客户端代理类。
// 为代理类添加命名空间,缺省为全局空间。
CodeNamespace nmspace = new CodeNamespace();
CodeCompileUnit unit = new CodeCompileUnit();
unit.Namespaces.Add(nmspace);
ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit);
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters parameter = new CompilerParameters();
parameter.GenerateExecutable = false;
// 可以指定你所需的任何文件名。
parameter.OutputAssembly = filePath + className + "_" + methodName + ".dll";
parameter.ReferencedAssemblies.Add("System.dll");
parameter.ReferencedAssemblies.Add("System.XML.dll");
parameter.ReferencedAssemblies.Add("System.Web.Services.dll");
parameter.ReferencedAssemblies.Add("System.Data.dll");
// 生成dll文件,并会把WebService信息写入到dll里面
CompilerResults result = provider.CompileAssemblyFromDom(parameter, unit);
if (result.Errors.HasErrors)
{
// 显示编译错误信息
System.Text.StringBuilder sb = new StringBuilder();
foreach (CompilerError ce in result.Errors)
{
sb.Append(ce.ToString());
sb.Append(System.Environment.NewLine);
}
throw new Exception(sb.ToString());
}
//记录缓存
var objCache = HttpRuntime.Cache;
// 缓存信息写入dll文件
objCache.Insert(className + "_" + methodName, "1", null, DateTime.Now.AddMinutes(5), TimeSpan.Zero, CacheItemPriority.High, null);
}
}
}

 (2)调用方法,前面四个配置信息可以写在app.config里,也可以直接代码里写死。

/// <param name="xml">要上传的参数</param>
private void UseWebService(string xml)
{
// 读取配置文件,获取配置信息
string url = ConfigurationManager.AppSettings["WebServiceAddress"];//WebService地址
string className = ConfigurationManager.AppSettings["ClassName"];//WebService提供的类名
string methodName = ConfigurationManager.AppSettings["MethodName"];// WebService方法名
string filePath = ConfigurationManager.AppSettings["FilePath"];//存放dll文件的地址
// 调用WebServiceHelper
WebServiceHelper.CreateWebServiceDLL(url, className, methodName, filePath);
// 读取dll内容
byte[] filedata = File.ReadAllBytes(filePath + className + "_" + methodName + ".dll");
// 加载程序集信息
Assembly asm = Assembly.Load(filedata);
Type t = asm.GetType(className);
// 创建实例
object o = Activator.CreateInstance(t);
MethodInfo method = t.GetMethod(methodName);
// 参数
object[] args = {xml};
// 调用访问,获取方法返回值
string value = method.Invoke(o, args).ToString();
//输出返回值
MessageBox.Show($"返回值:{value}");
}

 3.HttpWebRequest调用

一开始我使用上面两种方法,总是返回【Fault occurred while processing】这个错误,但是使用postMan是可以调试成功的。因为是远程开发,没法直接用链接添加服务引用,大概是对方用Java写的WebService,而我是用C#调用的导致的。后来找到一个大神的帖子,发现可以这样调用,顺利解决了问题。

其实就是按照postMan调试的格式,将要上传的xml字符串拼接在里面,里面还有个方法是为了解决【SSL/TLS】的问题,代码如下:

(1)代码

/// <param name="xml">要上传的参数</param>

private string UseHttpwebRequest(string xml)
{
string responseString = string.Empty;//返回内容
// SOAP格式内容,参数为:xml
StringBuilder param = new StringBuilder();
param.Append("<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ser=\"http://service.ws.domain.jkjczx.ohms.zjcdjk.cn/\">\r\n");
param.Append("<soapenv:Header/>\r\n");
param.Append("<soapenv:Body>\r\n");
param.Append("<ser:putData>\r\n");
param.Append("<arg0><![CDATA[");
param.Append(xml);
param.Append("]]></arg0>\r\n");
param.Append("</ser:putData>\r\n</soapenv:Body>\r\n</soapenv:Envelope>");
try
{
ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckValidationResult);//验证服务器证书回调自动验证
// 创建HttpWebRequest对象
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(webServiceUrl);// webservice调用地址
// 设置POST调用方法
httpRequest.Method = "POST";
// 设置HTTP头ContentType
httpRequest.ContentType = "text/xml;charset=UTF-8";
// 设置HTTP头SOAPAction的值
//httpRequest.Headers.Add("SOAPAction", "urn:world");//未使用到
// 调用内容
byte[] bytes = Encoding.UTF8.GetBytes(param.ToString());
// 设置HTTP头内容的长度
//httpRequest.ContentLength = param.ToString().Length;//未使用到
using (Stream reqStream = httpRequest.GetRequestStream())
{
reqStream.Write(bytes, 0, bytes.Length);
reqStream.Flush();
}
// HttpWebRequest发起调用
using (HttpWebResponse myResponse = (HttpWebResponse)httpRequest.GetResponse())
{
StreamReader sr = new StreamReader(myResponse.GetResponseStream(), Encoding.UTF8);// StreamReader对象
responseString = sr.ReadToEnd(); // 返回结果
}
}
catch (Exception ex)
{
responseString = "";
}
return responseString;
}

//这个方法依然为了解决【SSL/TLS】问题
public static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{
return true;//直接确认,否则打不开
}

 

以上就是我在网上找到的三种方法。

 

 

====================================================================

=====================来自2021年的更新内容=============================

另一家客户的业务,同样需要调用webService上传报文,一开始用了上面的第三种方法【HttpWebRequest】,结果返回【远程服务器返回错误: (500) 内部服务器错误。】这个错误,postMan带报文调试,一点问题没有,C#后台调用就崩了,重新使用静态方法传,结果返回值直接是空字符串,这个报文也跟以前开发的不一样,没有【CDATA】,直接就是拼接在一起的字符串,最后网上找到其他办法,终于解决,其实只改动了一个地方,现将解决方法整理如下:

*方法就是上面的HttpWebRequest调用的方法,只是把httpRequest.ContentType改成"application/soap+xml; charset=utf-8"

*也有种说法,要设置httpRequest.Headers.Add("SOAPAction", "urn:world"),但是客户提供的webService没有给到SOAPAction的内容,原来我也没用过,所以无法验证是否有用。

报文格式:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:pojo="http://platform.wsxxzx.com">
<soapenv:Header/>
<soapenv:Body>
<pushPERReq xmlns="http://platform.wsxxzx.com">
<msgId>201711</msgId>
<creationTime>20170406152512</creationTime>

......

</pushPERReq>
</soapenv:Body>
</soapenv:Envelope>

*以上方法是参照下面这个SOAP1.2 协议修改的,接触这个的时间不长,具体的原理机制也不是太清楚,先记载在这里,给个参考。

PS:以下内容转载自狗尾草的博文,原文地址:https://www.cnblogs.com/macroxu-1982/archive/2009/12/23/1630415.html

1. WebService支持的交互协议

WebService支持三种方式

1)Http post 方式(注意这种方式只对于本机调试使用,在web服务部署在其他机器上,应用程序不能通过 Http Post方式调用)

       具体交互格式如下:

  POST /WebServiceTest/Service1.asmx/HelloWorld HTTP/1.1

  Host: localhost

  Content-Type: application/x-www-form-urlencoded

  Content-Length: length

 

  StudentName=string&PassWord=string

 

2)SOAP1.1协议 注意Soap协议是基于HTTP的协议,也就是在HTTP的基础上再次封装

  交互格式如下:

  POST /WebServiceTest/Service1.asmx HTTP/1.1

  Host: localhost

  Content-Type: text/xml; charset=utf-8

  Content-Length: length

  SOAPAction: "http://tempuri.org/HelloWorld"

 

  <?xml version="1.0" encoding="utf-8"?>

  <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">

    <soap:Body>

      <HelloWorld xmlns="http://tempuri.org/">

        <StudentName>string</StudentName>

        <PassWord>string</PassWord>

      </HelloWorld>

    </soap:Body>

  </soap:Envelope>

 

3)SOAP1.2 协议

  交互格式如下:

  POST /WebServiceTest/Service1.asmx HTTP/1.1

  Host: localhost

  Content-Type: application/soap+xml; charset=utf-8

  Content-Length: length

 

  <?xml version="1.0" encoding="utf-8"?>

  <soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">

    <soap12:Body>

      <HelloWorld xmlns="http://tempuri.org/">

        <StudentName>string</StudentName>

        <PassWord>string</PassWord>

      </HelloWorld>

    </soap12:Body>

  </soap12:Envelope>

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM