C#調(diào)用webservice接口的最新方法教程
前言
Web Service也叫XML Web Service WebService是一種可以接收從Internet或者Intranet上的其它系統(tǒng)中傳遞過(guò)來(lái)的請(qǐng)求,輕量級(jí)的獨(dú)立的通訊技術(shù)。是:通過(guò)SOAP在Web上提供的軟件服務(wù),使用WSDL文件進(jìn)行說(shuō)明,并通過(guò)UDDI進(jìn)行注冊(cè)。
XML:(Extensible Markup Language)擴(kuò)展型可標(biāo)記語(yǔ)言。面向短期的臨時(shí)數(shù)據(jù)處理、面向萬(wàn)維網(wǎng)絡(luò),是Soap的基礎(chǔ)。
Soap:(Simple Object Access Protocol)簡(jiǎn)單對(duì)象存取協(xié)議。是XML Web Service 的通信協(xié)議。當(dāng)用戶通過(guò)UDDI找到你的WSDL描述文檔后,他通過(guò)可以SOAP調(diào)用你建立的Web服務(wù)中的一個(gè)或多個(gè)操作。SOAP是XML文檔形式的調(diào)用方法的規(guī)范,它可以支持不同的底層接口,像HTTP(S)或者SMTP。
WSDL:(Web Services Description Language) WSDL 文件是一個(gè) XML 文檔,用于說(shuō)明一組 SOAP 消息以及如何交換這些消息。大多數(shù)情況下由軟件自動(dòng)生成和使用。
UDDI (Universal Description, Discovery, and Integration) 是一個(gè)主要針對(duì)Web服務(wù)供應(yīng)商和使用者的新項(xiàng)目。在用戶能夠調(diào)用Web服務(wù)之前,必須確定這個(gè)服務(wù)內(nèi)包含哪些商務(wù)方法,找到被調(diào)用的接口定義,還要在服務(wù)端來(lái)編制軟件,UDDI是一種根據(jù)描述文檔來(lái)引導(dǎo)系統(tǒng)查找相應(yīng)服務(wù)的機(jī)制。UDDI利用SOAP消息機(jī)制(標(biāo)準(zhǔn)的XML/HTTP)來(lái)發(fā)布,編輯,瀏覽以及查找注冊(cè)信息。它采用XML格式來(lái)封裝各種不同類型的數(shù)據(jù),并且發(fā)送到注冊(cè)中心或者由注冊(cè)中心來(lái)返回需要的數(shù)據(jù)。
C#調(diào)用webservice接口
我們?cè)谌粘i_(kāi)發(fā)中,經(jīng)常遇到C#調(diào)用webservice的情況,通常來(lái)說(shuō)如果webservice是用vs+c#來(lái)開(kāi)發(fā)的,問(wèn)題一般來(lái)說(shuō)不大,直接web引用,然后調(diào)用就OK了。
流程如下:




下面就是進(jìn)行調(diào)用,就這么簡(jiǎn)單。

但如果webservice是用JAVA或者其它語(yǔ)言或者其它工具生成的話,使用vs+c#來(lái)調(diào)用就經(jīng)常遇到問(wèn)題;就是使用上面的方法顯得很不好使,經(jīng)常是使用SOAP UI調(diào)用沒(méi)有問(wèn)題,但使用上面的方法卻調(diào)用報(bào)錯(cuò),經(jīng)常是500的錯(cuò)誤。當(dāng)你聯(lián)系webservice提供商時(shí)通常會(huì)說(shuō)SOAP UI都能調(diào)用得到,你們用代碼為啥子調(diào)用不到,問(wèn)題出在你們的調(diào)用方法上。
在我們向其它公司提供webservice的時(shí)候,經(jīng)常也會(huì)出現(xiàn)這樣的問(wèn)題,以前我們一直都以為SOAP UI能夠調(diào)用,那么代碼也就一定能夠調(diào)用得通,但經(jīng)過(guò)實(shí)踐,我們自己寫DEMO調(diào)用自己的webservice時(shí)才發(fā)現(xiàn),并不是別人的調(diào)用代碼寫的有問(wèn)題,因?yàn)槲覀冏约阂矡o(wú)法將自己寫的webservice調(diào)用得通,或者說(shuō)沒(méi)有找到正確的方法調(diào)用得通。
這時(shí)我們就要思考是否是SOAP UI能夠調(diào)用得通的webservice就代碼一定調(diào)用沒(méi)有問(wèn)題呢?或者說(shuō)SOAP UI調(diào)用webservice和代碼調(diào)用webservice的原理區(qū)別到底在哪里呢?
總結(jié)一下:
(1)SOAP UI能夠調(diào)用成功,代碼不一定能夠調(diào)用成功,代碼調(diào)用成功并且得到返回結(jié)果的前提是webservice可以按標(biāo)準(zhǔn)返回結(jié)果,但SOAP UI是只要按信封返回就可以收到結(jié)果而不管結(jié)果是否標(biāo)準(zhǔn);
(2)如果webservice的header有用戶名和密碼的校驗(yàn),使用SOAP UI可以調(diào)用成功并且得到返回結(jié)果,但使用上面web引用的方式卻不行。對(duì)于這種情況,有以下方法可以調(diào)用成功:
重復(fù)上面web引用的方式,另外引用Microsoft.Web.Services3(這個(gè)DLL可以在這里下載)。

手工修改Reference.cs


以下是調(diào)用方法,重點(diǎn)在傳入用戶名和密碼,修改繼承類的重點(diǎn)也在于此;

引用完成,調(diào)用就會(huì)成功了,結(jié)果如下:

從上面可以看出web引用真的是簡(jiǎn)單粗暴,基本不用寫幾句代碼就可以搞定絕大部分的webservice調(diào)用。
但是web引用卻有在現(xiàn)實(shí)開(kāi)發(fā)中常遇到的缺點(diǎn):
(1)需要開(kāi)發(fā)環(huán)境可以訪問(wèn)到的wsdl地址;
(2)如果webservice有變化需要更新web引用;

那有沒(méi)有其它方法呢?答案是有的。一種是直接封裝信封,使用WebRequest,代碼如下:
private void button2_Click(object sender, EventArgs e)
{
StringBuilder soap = new StringBuilder();
soap.Append("<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:zls=\"www.zlsoft.cn\">");
soap.Append("<soap:Header>");
soap.Append("<wsse:Security soap:mustUnderstand=\"true\" xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">");
soap.Append("<wsse:UsernameToken wsu:Id=\"UsernameToken-4\">");
soap.Append("<wsse:Username>hjq</wsse:Username>");//用戶名
soap.Append("<wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">123</wsse:Password>");//口令
soap.Append("<wsse:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\">gC6dDGKjxo0IaRc5KpQU3w==</wsse:Nonce>");
soap.Append("<wsu:Created>2017-11-01T05:11:22.805Z</wsu:Created>");
soap.Append("</wsse:UsernameToken>");
soap.Append("</wsse:Security>");
soap.Append("</soap:Header>");
soap.Append("<soap:Body>");
soap.Append("<zls:getUserInfo>");
soap.Append("<zls:appsys_id>trwetre</zls:appsys_id>");
soap.Append("<zls:appsys_token>sdafdsf</zls:appsys_token>");
soap.Append("<zls:sid>fdsafds</zls:sid>");
soap.Append("</zls:getUserInfo>");
soap.Append("</soap:Body>");
soap.Append("</soap:Envelope>");
string url = "http://127.0.0.1:6998/services/HIPService?wsdl";
var result = GetSOAPReSource(url, soap.ToString());
}
public static string GetSOAPReSource(string url, string datastr)
{
try
{
//request
Uri uri = new Uri(url);
WebRequest webRequest = WebRequest.Create(uri);
webRequest.ContentType = "application/soap+xml; charset=utf-8";
webRequest.Method = "POST";
using (Stream requestStream = webRequest.GetRequestStream())
{
byte[] paramBytes = Encoding.UTF8.GetBytes(datastr.ToString());
requestStream.Write(paramBytes, 0, paramBytes.Length);
}
//response
WebResponse webResponse = webRequest.GetResponse();
using (StreamReader myStreamReader = new StreamReader(webResponse.GetResponseStream(), Encoding.UTF8))
{
string result = "";
return result = myStreamReader.ReadToEnd();
}
}
catch (Exception ex)
{
throw ex;
}
}
但是上述方式還是存在問(wèn)題,如果webservice需要校驗(yàn)header里面的用戶名與密碼時(shí),雖然信封中封裝了用戶名和密碼,但是一樣調(diào)用不成功,同樣的信封內(nèi)容,使用SOAP UI卻能成功??梢?jiàn)代碼調(diào)用和SOAP UI調(diào)用還是有一定區(qū)別的。
另外一種方式是使用動(dòng)態(tài)編譯:
Uri uri = new Uri(txt_url.Text);
WebRequest webRequest = WebRequest.Create(uri);
webRequest.Credentials = new NetworkCredential("hjq", "123");
System.IO.Stream requestStream = webRequest.GetResponse().GetResponseStream();
// Get a WSDL file describing a service
ServiceDescription sd = ServiceDescription.Read(requestStream);
string sdName = sd.Services[0].Name;
// Add in tree view
// Initialize a service description servImport
ServiceDescriptionImporter servImport = new ServiceDescriptionImporter();
servImport.AddServiceDescription(sd, String.Empty, String.Empty);
servImport.ProtocolName = "Soap";
servImport.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties;
CodeNamespace nameSpace = new CodeNamespace();
CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
codeCompileUnit.Namespaces.Add(nameSpace);
// Set Warnings
ServiceDescriptionImportWarnings warnings = servImport.Import(nameSpace, codeCompileUnit);
if (warnings == 0)
{
StringWriter stringWriter = new StringWriter(System.Globalization.CultureInfo.CurrentCulture);
Microsoft.CSharp.CSharpCodeProvider prov = new Microsoft.CSharp.CSharpCodeProvider();
prov.GenerateCodeFromNamespace(nameSpace, stringWriter, new CodeGeneratorOptions());
// Compile the assembly with the appropriate references
//string[] assemblyReferences = new string[2] { "System.Web.Services.dll", "System.Xml.dll" };
string[] assemblyReferences = new string[3] {"System.dll", "System.dll", "System.dll" };
CompilerParameters param = new CompilerParameters(assemblyReferences);
param.GenerateExecutable = false;
param.GenerateInMemory = true;
param.TreatWarningsAsErrors = false;
param.WarningLevel = 4;
CompilerResults results = new CompilerResults(new TempFileCollection());
results = prov.CompileAssemblyFromDom(param, codeCompileUnit);
Assembly assembly = results.CompiledAssembly;
Type service = assembly.GetType(sdName);
MethodInfo[] methodInfo = service.GetMethods();
foreach (MethodInfo t in methodInfo)
{
if (t.Name == "Discover")
break;
if (t.Name == cbMethods.Text)
{
//MessageBox.Show(t.ToString());
//Invoke Method
Object obj = Activator.CreateInstance(service);
Object response = t.Invoke(obj, new object[] { "1","2","3"});
MessageBox.Show("Result = " + response.ToString());
break;
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
從這種方式可以看出,首先是訪問(wèn)wsdl的地址取回webservice的結(jié)構(gòu),然后采取動(dòng)態(tài)編譯生成本地程序集的方式去調(diào)用webservice,和web引用自動(dòng)生成Reference.cs的原理類似;但是明顯這種方式也存在問(wèn)題,如果webservice的header中需要校驗(yàn)用戶名與密碼怎么處理呢?
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
C#實(shí)現(xiàn)DataList里面嵌套DataList的折疊菜單
這篇文章主要介紹了C#實(shí)現(xiàn)DataList里面嵌套DataList的折疊菜單,以實(shí)例形式詳細(xì)分析了DataList嵌套實(shí)現(xiàn)折疊菜單所涉及的JavaScript、HTML與C#相關(guān)使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11
C#中一個(gè)方法返回多個(gè)值的實(shí)現(xiàn)方法小結(jié)
通常一個(gè)方法只能返回一個(gè)值,但是如果在某些時(shí)候,我們想要返回多個(gè)值,例如某個(gè)方法將一個(gè)浮點(diǎn)數(shù)分割成一個(gè)整數(shù)和一個(gè)小數(shù)返回,因此本文給大家介紹了C#中一個(gè)方法返回多個(gè)值的實(shí)現(xiàn)方法及示例代碼,需要的朋友可以參考下2024-05-05
C#動(dòng)態(tài)調(diào)整數(shù)組大小的方法
這篇文章主要介紹了C#動(dòng)態(tài)調(diào)整數(shù)組大小的方法,涉及C#中靜態(tài)方法CreateInstance的使用技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04
WPF自定義實(shí)現(xiàn)雷達(dá)圖控件的示例詳解
雷達(dá)圖用于表示不同內(nèi)容的占比關(guān)系,在項(xiàng)目中有廣泛的應(yīng)用,但是目前未曾有封裝良好的雷達(dá)圖控件,所以本文分享了如何封裝一個(gè)通用的雷達(dá)圖控件,希望對(duì)大家有所幫助2023-08-08
C#中動(dòng)態(tài)數(shù)組用法實(shí)例
這篇文章主要介紹了C#中動(dòng)態(tài)數(shù)組用法,實(shí)例分析了C#中ArrayList實(shí)現(xiàn)動(dòng)態(tài)數(shù)組的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04
UnityShader使用Plane實(shí)現(xiàn)翻書(shū)效果
這篇文章主要為大家詳細(xì)介紹了UnityShader使用Plane實(shí)現(xiàn)翻書(shū)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07

