你的位置:首页 > ASP.net教程

[ASP.net教程]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信息并创建代理类的程序集。 

 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."); 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         else104         {105           error = "打开WebServiceUrl失败";106         }107       }108       catch (Exception ex)109       {110         error = ex.Message;111       }112       return false;113     }114 115     /// <summary>116     /// 反射构建Methods117     /// </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的一个专用版本,遵循一种特殊的

 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+"; 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             else106               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+";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     /// 验证URL142     /// </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的两种不同的风格。就安全性等方面来说,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