站在開發者的角度,WebService 技術確實是再也不「時髦」。甚至不少人會說,咱們再也不用它。固然,爲了使軟件能夠更簡潔,更有層次,更易於實現緩存等機制,我是很是建議將 SOAP 轉爲 RESTful 架構風格的。但到目前爲止,WebService 在一些Public Institution 中使用仍是十分普遍的。javascript
這裏主要討論一下關於WebService的調用問題。關於WebService 的調用分爲靜態調用和動態調用兩種。php
靜態調用的方式是經過「Add Service Reference...」建立客戶端代理類。這種方式讓VS.NET環境來爲咱們生成服務代理,而後調用對應的Web服務。這樣是使工做簡單了,可是卻將提供Web服務的URL、方法名、參數綁定在一塊兒了,這是VS.NET自動爲咱們生成Web服務代理的限制。若是發佈Web服務的URL改變了,則咱們須要從新讓VS.NET生成代理,並從新編譯。很常見的一個場景,某銀行Web服務,由於部署的URL更改,而不得不去從新編譯生成代理,這將會帶來不少沒必要要的工做量。若是咱們使用動態調用就能夠避免這種狀況。關於靜態調用,不是這篇文章的重點,故不做詳細介紹。html
在某些狀況下咱們須要在程序運行期間動態調用一個服務。在 .NET Framework 的 System.Web.Services.Description 命名空間中有咱們須要的東西。動態調用有動態調用 WebService、生成客戶端代理程序集文件、生成客戶端代理類源代碼3種方式。java
動態調用的具體步驟爲:linux
1)從目標 URL 下載 WSDL 數據;web
2)使用 ServiceDescription 建立和格式化 WSDL 文檔文件;json
3)使用 ServiceDescriptionImporter 建立客戶端代理類;緩存
4)使用 CodeDom 動態建立客戶端代理類程序集;安全
5)利用反射調用相關 WebService 方法。restful
第一種方式經過在內存中建立動態程序集的方式完成了動態調用過程;第二種方式將客戶端代理類生成程序集文件保存到硬盤,而後能夠經過 Assembly.LoadFrom() 載入並進行反射調用。對於須要屢次調用的系統,要比每次生成動態程序集效率高出不少;第三種方式是保存源碼文件到硬盤中,而後再進行反射調用。
這裏將只討論第二種方式,這種方式也是咱們在實際應用中最經常使用的。這種方式只下載 一次 WSDL 信息並建立代理類的程序集。日後程序每次啓動都會反射以前建立好的程序集。若是是 Web服務 URL 變動,只須要修改 App.config 中的 WebServiceUrl 和 ProxyClassName 配置項,並將程序根目錄下生成的程序集刪除便可。下次程序啓動又會從新下載WSDL信息並建立代理類的程序集。
App.config文件。
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <appSettings> 4 <!--WebService地址--> 5 <add key="WebServiceUrl" value="http://localhost:25060/testService/" /> 6 <!--WebService輸出dll文件名稱--> 7 <add key="OutputDllFilename" value="TestWebService.dll" /> 8 <!--WebService代理類名稱--> 9 <add key="ProxyClassName" value="TestService" /> 10 </appSettings> 11 </configuration>
建立代理類。
1 public class WSHelper 2 { 3 /// <summary>
4 /// 輸出的dll文件名稱 5 /// </summary>
6 private static string m_OutputDllFilename; 7
8 /// <summary>
9 /// WebService代理類名稱 10 /// </summary>
11 private static string m_ProxyClassName; 12
13 /// <summary>
14 /// WebService代理類實例 15 /// </summary>
16 private static object m_ObjInvoke; 17
18 /// <summary>
19 /// 接口方法字典 20 /// </summary>
21 private static Dictionary<EMethod, MethodInfo> m_MethodDic = new Dictionary<EMethod, MethodInfo>(); 22
23 /// <summary>
24 /// 建立WebService,生成客戶端代理程序集文件 25 /// </summary>
26 /// <param name="error">錯誤信息</param>
27 /// <returns>返回:true或false</returns>
28 public static bool CreateWebService(out string error) 29 { 30 try
31 { 32 error = string.Empty; 33 m_OutputDllFilename = ConfigurationManager.AppSettings["OutputDllFilename"]; 34 m_ProxyClassName = ConfigurationManager.AppSettings["ProxyClassName"]; 35 string webServiceUrl = ConfigurationManager.AppSettings["WebServiceUrl"]; 36 webServiceUrl += "?WSDL"; 37
38 // 若是程序集已存在,直接使用
39 if (File.Exists(Path.Combine(Environment.CurrentDirectory, m_OutputDllFilename))) 40 { 41 BuildMethods(); 42 return true; 43 } 44
45 //使用 WebClient 下載 WSDL 信息。
46 WebClient web = new WebClient(); 47 Stream stream = web.OpenRead(webServiceUrl); 48
49 //建立和格式化 WSDL 文檔。
50 if (stream != null) 51 { 52 // 格式化WSDL
53 ServiceDescription description = ServiceDescription.Read(stream); 54
55 // 建立客戶端代理類。
56 ServiceDescriptionImporter importer = new ServiceDescriptionImporter 57 { 58 ProtocolName = "Soap", 59 Style = ServiceDescriptionImportStyle.Client, 60 CodeGenerationOptions =
61 CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync 62 }; 63
64 // 添加 WSDL 文檔。
65 importer.AddServiceDescription(description, null, null); 66
67 //使用 CodeDom 編譯客戶端代理類。
68 CodeNamespace nmspace = new CodeNamespace(); 69 CodeCompileUnit unit = new CodeCompileUnit(); 70 unit.Namespaces.Add(nmspace); 71
72 ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit); 73 CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp"); 74
75 CompilerParameters parameter = new CompilerParameters 76 { 77 GenerateExecutable = false, 78 // 指定輸出dll文件名。
79 OutputAssembly = m_OutputDllFilename 80 }; 81
82 parameter.ReferencedAssemblies.Add("System.dll"); 83 parameter.ReferencedAssemblies.Add("System.XML.dll"); 84 parameter.ReferencedAssemblies.Add("System.Web.Services.dll"); 85 parameter.ReferencedAssemblies.Add("System.Data.dll"); 86
87 // 編譯輸出程序集
88 CompilerResults result = provider.CompileAssemblyFromDom(parameter, unit); 89
90 // 使用 Reflection 調用 WebService。
91 if (!result.Errors.HasErrors) 92 { 93 BuildMethods(); 94 return true; 95 } 96 else
97 { 98 error = "反射生成dll文件時異常"; 99 } 100 stream.Close(); 101 stream.Dispose(); 102 } 103 else
104 { 105 error = "打開WebServiceUrl失敗"; 106 } 107 } 108 catch (Exception ex) 109 { 110 error = ex.Message; 111 } 112 return false; 113 } 114
115 /// <summary>
116 /// 反射構建Methods 117 /// </summary>
118 private static void BuildMethods() 119 { 120 Assembly asm = Assembly.LoadFrom(m_OutputDllFilename); 121 //var types = asm.GetTypes();
122 Type asmType = asm.GetType(m_ProxyClassName); 123 m_ObjInvoke = Activator.CreateInstance(asmType); 124
125 //var methods = asmType.GetMethods();
126 var methods = Enum.GetNames(typeof(EMethod)).ToList(); 127 foreach (var item in methods) 128 { 129 var methodInfo = asmType.GetMethod(item); 130 if (methodInfo != null) 131 { 132 var method = (EMethod)Enum.Parse(typeof(EMethod), item); 133 m_MethodDic.Add(method, methodInfo); 134 } 135 } 136 } 137
138 /// <summary>
139 /// 獲取請求響應 140 /// </summary>
141 /// <param name="method">方法</param>
142 /// <param name="para">參數</param>
143 /// <returns>返回:Json串</returns>
144 public static string GetResponseString(EMethod method, params object[] para) 145 { 146 string result = null; 147 if (m_MethodDic.ContainsKey(method)) 148 { 149 var temp = m_MethodDic[method].Invoke(m_ObjInvoke, para); 150 if (temp != null) 151 { 152 result = temp.ToString(); 153 } 154 } 155 return result; 156 } 157 }
調用接口。
1 // SOAP 請求響應方式
2 TextBox3.Text = WSHelper.GetResponseString(EMethod.Add, Convert.ToInt32(TextBox1.Text), Convert.ToInt32(TextBox2.Text));
除了靜態調用和動態調用,咱們還能夠發送HttpPost請求來調用WebService的方法。Soap請求就是HTTP POST的一個專用版本,遵循一種特殊的xml消息格式。使用HttpPost請求,對返回結果咱們能夠手動解析。下面的實現其實和調用WebAPI是徹底同樣的。
1 /// <summary>
2 /// 請求信息幫助 3 /// </summary>
4 public partial class HttpHelper 5 { 6 private static HttpHelper m_Helper; 7 /// <summary>
8 /// 單例 9 /// </summary>
10 public static HttpHelper Helper 11 { 12 get { return m_Helper ?? (m_Helper = new HttpHelper()); } 13 } 14
15 /// <summary>
16 /// 獲取請求的數據 17 /// </summary>
18 /// <param name="strUrl">請求地址</param>
19 /// <param name="requestMode">請求方式</param>
20 /// <param name="parameters">參數</param>
21 /// <param name="requestCoding">請求編碼</param>
22 /// <param name="responseCoding">響應編碼</param>
23 /// <param name="timeout">請求超時時間(毫秒)</param>
24 /// <returns>返回:請求成功響應信息,失敗返回null</returns>
25 public string GetResponseString(string strUrl, ERequestMode requestMode, Dictionary<string, string> parameters, Encoding requestCoding, Encoding responseCoding, int timeout = 300) 26 { 27 string url = VerifyUrl(strUrl); 28 HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(new Uri(url)); 29
30 HttpWebResponse webResponse = null; 31 switch (requestMode) 32 { 33 case ERequestMode.Get: 34 webResponse = GetRequest(webRequest, timeout); 35 break; 36 case ERequestMode.Post: 37 webResponse = PostRequest(webRequest, parameters, timeout, requestCoding); 38 break; 39 } 40
41 if (webResponse != null && webResponse.StatusCode == HttpStatusCode.OK) 42 { 43 using (Stream newStream = webResponse.GetResponseStream()) 44 { 45 if (newStream != null) 46 using (StreamReader reader = new StreamReader(newStream, responseCoding)) 47 { 48 string result = reader.ReadToEnd(); 49 return result; 50 } 51 } 52 } 53 return null; 54 } 55
56
57 /// <summary>
58 /// get 請求指定地址返回響應數據 59 /// </summary>
60 /// <param name="webRequest">請求</param>
61 /// <param name="timeout">請求超時時間(毫秒)</param>
62 /// <returns>返回:響應信息</returns>
63 private HttpWebResponse GetRequest(HttpWebRequest webRequest, int timeout) 64 { 65 try
66 { 67 webRequest.Accept = "text/html, application/xhtml+xml, application/json, text/javascript, */*; q=0.01"; 68 webRequest.Headers.Add("Accept-Language", "zh-cn,en-US,en;q=0.5"); 69 webRequest.Headers.Add("Cache-Control", "no-cache"); 70 webRequest.UserAgent = "DefaultUserAgent"; 71 webRequest.Timeout = timeout; 72 webRequest.Method = "GET"; 73
74 // 接收返回信息
75 HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse(); 76 return webResponse; 77 } 78 catch (Exception ex) 79 { 80 return null; 81 } 82 } 83
84
85 /// <summary>
86 /// post 請求指定地址返回響應數據 87 /// </summary>
88 /// <param name="webRequest">請求</param>
89 /// <param name="parameters">傳入參數</param>
90 /// <param name="timeout">請求超時時間(毫秒)</param>
91 /// <param name="requestCoding">請求編碼</param>
92 /// <returns>返回:響應信息</returns>
93 private HttpWebResponse PostRequest(HttpWebRequest webRequest, Dictionary<string, string> parameters, int timeout, Encoding requestCoding) 94 { 95 try
96 { 97 // 拼接參數
98 string postStr = string.Empty; 99 if (parameters != null) 100 { 101 parameters.All(o =>
102 { 103 if (string.IsNullOrEmpty(postStr)) 104 postStr = string.Format("{0}={1}", o.Key, o.Value); 105 else
106 postStr += string.Format("&{0}={1}", o.Key, o.Value); 107
108 return true; 109 }); 110 } 111
112 byte[] byteArray = requestCoding.GetBytes(postStr); 113 webRequest.Accept = "text/html, application/xhtml+xml, application/json, text/javascript, */*; q=0.01"; 114 webRequest.Headers.Add("Accept-Language", "zh-cn,en-US,en;q=0.5"); 115 webRequest.Headers.Add("Cache-Control", "no-cache"); 116 webRequest.UserAgent = "DefaultUserAgent"; 117 webRequest.Timeout = timeout; 118 webRequest.ContentType = "application/x-www-form-urlencoded"; 119 webRequest.ContentLength = byteArray.Length; 120 webRequest.Method = "POST"; 121
122 // 將參數寫入流
123 using (Stream newStream = webRequest.GetRequestStream()) 124 { 125 newStream.Write(byteArray, 0, byteArray.Length); 126 newStream.Close(); 127 } 128
129 // 接收返回信息
130 HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse(); 131 return webResponse; 132 } 133 catch (Exception ex) 134 { 135 return null; 136 } 137 } 138
139
140 /// <summary>
141 /// 驗證URL 142 /// </summary>
143 /// <param name="url">待驗證 URL</param>
144 /// <returns></returns>
145 private string VerifyUrl(string url) 146 { 147 if (string.IsNullOrEmpty(url)) 148 throw new Exception("URL 地址不能夠爲空!"); 149
150 if (url.StartsWith("http://", StringComparison.CurrentCultureIgnoreCase)) 151 return url; 152
153 return string.Format("http://{0}", url); 154 } 155 }
HttpPost 請求響應方式調用接口。
1 // Http Post 請求響應方式
2 string url = m_WebServiceUrl + EMethod.Add.ToString(); //@"http://localhost:25060/testService.asmx/Add";
3 Dictionary<string, string> parameters = new Dictionary<string, string> { { "parameter1", TextBox1.Text }, { "parameter2", TextBox2.Text } }; 4 string result = HttpHelper.Helper.GetResponseString(url, ERequestMode.Post, parameters, Encoding.Default, Encoding.UTF8); 5 XElement root = XElement.Parse(result); 6 TextBox3.Text = root.Value;
咱們都知道REST相比SOAP建議的標準更輕量級,甚到用Javascript均可以調用,使用方更方便、高效、簡單。但並非說REST就是SOAP的替代者。他們都只是實現Web Service(web服務)的兩種不一樣的架構風格。就安全性等方面來講,SOAP仍是更好的。
擴展閱讀
http://www.ruanyifeng.com/blog/2011/09/restful
http://www.cnblogs.com/lema/archive/2012/02/23/2364365.html
http://stevenjohn.iteye.com/blog/1442776
http://blog.linuxphp.org/archives/1505/
http://blog.csdn.net/superjj01/article/details/5270227
http://www.csdn.net/article/2013-06-13/2815744-RESTful-API