1. 程式人生 > >C#調用webservice

C#調用webservice

動態 stringbu 下載 use ret tree adt var utf8

經常遇到C#調用webservice的情況,通常來說如果webservice是用vs+c#來開發的,問題一般來說不大,直接web引用,然後調用就OK了。流程如下:

技術分享

技術分享

技術分享

技術分享

下面就是進行調用,就這麽簡單。

技術分享

但如果webservice是用JAVA或者其它語言或者其它工具生成的話,使用vs+c#來調用就經常遇到問題;就是使用上面的方法顯得很不好使,經常是使用SOAP UI調用沒有問題,但使用上面的方法卻調用報錯,經常是500的錯誤。當你聯系webservice提供商時通常會說SOAP UI都能調用得到,你們用代碼為啥子調用不到,問題出在你們的調用方法上。

在我們向其它公司提供webservice的時候,經常也會出現這樣的問題,以前我們一直都以為SOAP UI能夠調用,那麽代碼也就一定能夠調用得通,但經過實踐,我們自己寫DEMO調用自己的webservice時才發現,並不是別人的調用代碼寫的有問題,因為我們自己也無法將自己寫的webservice調用得通,或者說沒有找到正確的方法調用得通。這時我們就要思考是否是SOAP UI能夠調用得通的webservice就代碼一定調用沒有問題呢?或者說SOAP UI調用webservice和代碼調用webservice的原理區別到底在哪裏呢?

總結一下:

(1)SOAP UI能夠調用成功,代碼不一定能夠調用成功,代碼調用成功並且得到返回結果的前提是webservice可以按標準返回結果,但SOAP UI是只要按信封返回就可以收到結果而不管結果是否標準;

(2)如果webservice的header有用戶名和密碼的校驗,使用SOAP UI可以調用成功並且得到返回結果,但使用上面web引用的方式卻不行。對於這種情況,有以下方法可以調用成功:

重復上面web引用的方式,另外引用Microsoft.Web.Services3(這個DLL可以CSDN上面下載)。

技術分享

手工修改Reference.cs

技術分享

技術分享

以下是調用方法,重點在傳入用戶名和密碼,修改繼承類的重點也在於此;

技術分享

引用完成,調用就會成功了,結果如下:

技術分享

從上面可以看出web引用真的是簡單粗暴,基本不用寫幾句代碼就可以搞定絕大部分的webservice調用。

但是web引用卻有在現實開發中常遇到的缺點:

(1)需要開發環境可以訪問到的wsdl地址;

(2)如果webservice有變化需要更新web引用;

技術分享

那有沒有其它方法呢?答案是有的。一種是直接封裝信封,使用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; } }

但是上述方式還是存在問題,如果webservice需要校驗header裏面的用戶名與密碼時,雖然信封中封裝了用戶名和密碼,但是一樣調用不成功,同樣的信封內容,使用SOAP UI卻能成功。可見代碼調用和SOAP UI調用還是有一定區別的。

另外一種方式是使用動態編譯:

                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);
            }

從這種方式可以看出,首先是訪問wsdl的地址取回webservice的結構,然後采取動態編譯生成本地程序集的方式去調用webservice,和web引用自動生成Reference.cs的原理類似;但是明顯這種方式也存在問題,如果webservice的header中需要校驗用戶名與密碼怎麽處理呢?

C#調用webservice