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