C# WebService动态调用


前言

  站在开发者的角度,WebService 技术确实是不再“时髦”。甚至很多人会说,我们不再用它。当然,为了使软件可以更简洁,更有层次,更易于实现缓存等机制,我是非常建议将 SOAP 转为 RESTful 架构风格的。但到目前为止,WebService 在一些Public Institution 中使用还是十分广泛的。

  这里主要讨论一下关于WebService的调用问题。关于WebService 的调用分为静态调用和动态调用两种。

 

静态调用

  静态调用的方式是通过“Add Service Reference...”创建客户端代理类。这种方式让VS.NET环境来为我们生成服务代理,然后调用对应的Web服务。这样是使工作简单了,但是却将提供Web服务的URL、方法名、参数绑定在一起了,这是VS.NET自动为我们生成Web服务代理的限制。如果发布Web服务的URL改变了,则我们需要重新让VS.NET生成代理,并重新编译。很常见的一个场景,某银行Web服务,因为部署的URL更改,而不得不去重新编译生成代理,这将会带来很多不必要的工作量。如果我们使用动态调用就可以避免这种情况。关于静态调用,不是这篇文章的重点,故不作详细介绍。

 

动态调用

  在某些情况下我们需要在程序运行期间动态调用一个服务。在 .NET Framework 的 System.Web.Services.Description 命名空间中有我们需要的东西。动态调用有动态调用 WebService、生成客户端代理程序集文件、生成客户端代理类源代码3种方式。

动态调用的具体步骤为:

1)从目标 URL 下载 WSDL 数据;

2)使用 ServiceDescription 创建和格式化 WSDL 文档文件;

3)使用 ServiceDescriptionImporter 创建客户端代理类;

4)使用 CodeDom 动态创建客户端代理类程序集;

5)利用反射调用相关 WebService 方法。

  第一种方式通过在内存中创建动态程序集的方式完成了动态调用过程;第二种方式将客户端代理类生成程序集文件保存到硬盘,然后可以通过 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));

 

Http请求 

  除了静态调用和动态调用,我们还可以发送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;

   

关于SOAP和REST

  我们都知道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

http://www.haodaima.net/art/2003909

http://my.oschina.net/lilw/blog/170518

 

 


免责声明!

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



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