[转]与大家分享一些Web Service的经验
使用Web服务也有半年多了,虽然时间不长,但还是遇到了不少难题,在这里把我的一些经验拿出来给大家共同分享。
刚开始做Web服务的时候还觉得很轻松,感觉就跟写一般的组件没什么区别,而使用时跟引用普通的程序集一样的简单,这是因为Visual Studio替我们完成了许多不必要的繁琐的工作。虽然如此,但是很容易造成我们的过分依赖,而忽略了Web服务发布和消费的内部工作机制。但随着开发的深入,越来越多的问题摆到了我的面前,大概有以下几个:
1. 动态url地址的配置
在消费Web服务时,最初都是直接引用静态Url地址,后来发现当Web服务生产方的地址有所变化时,我的客户端消费程序(此程序也可能是消费Web服务的Web应用程序服务端)必须要重新更新Web服务,这样就会增大程序部署的难度。为了使消费程序更加灵活,于是我就在web.config中加入了一段appSettings的配置信息,将需要修改的Url放入此段配置中,然后打开在asp.net1.1工程中引用最初的静态Web服务地址时自动生成的代理类文件(通常是\Web References\’web服务名’\Reference.cs),将this.URL属性修改为从配置文件中读取刚配好的Url信息,如:
web.config :
<appSettings>
<add key="URL_AccountVerifyForWebservice" value="http://eai.ibss:9001/VerifyWebService/
/xxx.jws"/>
</appSettings>
Reference.cs :
public class AccountVerifyForWebservice : System.Web.Services.Protocols.SoapHttpClientProtocol {
public AccountVerifyForWebservice() {
this.Url = ConfigurationSettings.AppSettings["URL_AccountVerifyForWebservice"];
}

.
}
这样就降低了部署难度,因为在Web服务地址改变后,你不需要在开发环境中更新消费程序然后再重新部署到客户端,而只需修改客户端的web.config文件内容就可以了,你甚至还可以自己配置一个xml文件来列举所有可能的url地址,然后在代理类中枚举这些地址列表即可。
2. DNS解析问题
在一个项目中与Weblogic打交道,需要我的aspnet应用程序消费对方提供的web服务,虽然我很顺利的完成了Web引用,即通过disco发现了Web服务,自动下载了wsdl文件,并生成了代理类文件,也正常通过了编译,但是在运行时一旦开始invoke此web服务就会报错,仔细检查了代理类一切正常,很纳闷搞不懂为什么。后来有同事告诉我可能是DNS的原因,我这才知道Web服务的生产环境上建立了负载平衡,而其提供的DNS服务器负责将http://eai.ibss:9001/VerifyWebService/.../xxx .jws这样的以域名地址动态的解析到所有提供Web服务的负载平衡服务器上,部署环境中的机器都可以通过此DNS访问web服务。一开始,服务发布方提供给我的只是其中一台固定Web服务器的静态ip地址(如http://192.168.0.1:9001/VerifyWebService/.../xxx .jws),而wsdl文档中描述的soap调用地址是域名地址,引用时自动生成的代理类的Url属性自然就是域名地址了,而我的开发环境不能够访问DNS服务器,也就不能解析域名地址,所以在运行时会抱错,因为soap信息根本就没有发送到正确的Web服务器上去。这种开发,生产和部署环境的不同有时是非常令人头痛的~~
后来通过采用第一个问题中介绍的配置文件的解决方案就很有效地解决了目前这个问题,开发调试时使用静态地址,部署时更换为域名地址即可。
3. Web服务和Web应用程序的分离
最好不要在同一台生产服务器上同时部署web服务和消费此web服务的web应用程序,这样会造成不必要的性能瓶颈。当客户端请求一个web应用程序的某个页面时,服务器将占用一个http连接,同时当该页的生成或某个事件被触发时需要同步调用一个web服务,那么此时该服务器将增加一个http连接的占用,也就是说用户请求一次页面有可能会在服务器上同时造成两个http连接,若服务器本身的http连接数为1000个的话那么可能的实际用户连接数只有500个。
4. 避免使用非string型数据
尽量避免在Web服务中使用非string型的数据作为Web方法的参数或返回值,因为Java或者别的消费客户端可能并不能够正常解析int或arraylist这样的数据类型,而string型几乎是最通用的数据类型,至少与java能够正常交互。尽量不要提供DataSet这样的复杂数据类型,尽管网上已有许多解决方案,但我感觉都挺麻烦的,还不如将DataSet直接输出到一个二维string型数组中。
或者
using System;
using System.Net;
using System.IO;
using System.CodeDom;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Web.Services.Description;
using System.Web.Services.Protocols;
namespace HB.Common
{
/* 调用方式
* string url = "http://www.webservicex.net/globalweather.asmx" ;
* string[] args = new string[2] ;
* args[0] = "Hangzhou";
* args[1] = "China" ;
* object result = WebServiceHelper.InvokeWebService(url ,"GetWeather" ,args) ;
* Response.Write(result.ToString());
*/
public class WebServiceHelper
{
#region InvokeWebService
/// <summary>
/// 动态调用web服务
/// </summary>
/// <param name="url">WSDL服务地址</param>
/// <param name="methodname">方法名</param>
/// <param name="args">参数</param>
/// <returns></returns>
public static object InvokeWebService(string url, string methodname, object[] args)
{
return WebServiceHelper.InvokeWebService(url, null, methodname, args);
}
/// <summary>
/// 动态调用web服务
/// </summary>
/// <param name="url">WSDL服务地址</param>
/// <param name="classname">类名</param>
/// <param name="methodname">方法名</param>
/// <param name="args">参数</param>
/// <returns></returns>
public static object InvokeWebService(string url, string classname, string methodname, object[] args)
{
string @namespace = "EnterpriseServerBase.WebService.DynamicWebCalling";
if ((classname == null) || (classname == ""))
{
classname = WebServiceHelper.GetWsClassName(url);
}
try
{
//获取WSDL
WebClient wc = new WebClient();
Stream stream = wc.OpenRead(url + "?WSDL");
ServiceDescription sd = ServiceDescription.Read(stream);
ServiceDescriptionImporter sdi = new ServiceDescriptionImporter();
sdi.AddServiceDescription(sd, "", "");
CodeNamespace cn = new CodeNamespace(@namespace);
//生成客户端代理类代码
CodeCompileUnit ccu = new CodeCompileUnit();
ccu.Namespaces.Add(cn);
sdi.Import(cn, ccu);
CSharpCodeProvider icc = new CSharpCodeProvider();
//设定编译参数
CompilerParameters cplist = new CompilerParameters();
cplist.GenerateExecutable = false;
cplist.GenerateInMemory = true;
cplist.ReferencedAssemblies.Add("System.dll");
cplist.ReferencedAssemblies.Add("System.XML.dll");
cplist.ReferencedAssemblies.Add("System.Web.Services.dll");
cplist.ReferencedAssemblies.Add("System.Data.dll");
//编译代理类
CompilerResults cr = icc.CompileAssemblyFromDom(cplist, ccu);
if (true == cr.Errors.HasErrors)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
foreach (System.CodeDom.Compiler.CompilerError ce in cr.Errors)
{
sb.Append(ce.ToString());
sb.Append(System.Environment.NewLine);
}
throw new Exception(sb.ToString());
}
//生成代理实例,并调用方法
System.Reflection.Assembly assembly = cr.CompiledAssembly;
Type t = assembly.GetType(@namespace + "." + classname, true, true);
object obj = Activator.CreateInstance(t);
System.Reflection.MethodInfo mi = t.GetMethod(methodname);
return mi.Invoke(obj, args);
/*
PropertyInfo propertyInfo = type.GetProperty(propertyname);
return propertyInfo.GetValue(obj, null);
*/
}
catch (Exception ex)
{
throw new Exception(ex.InnerException.Message, new Exception(ex.InnerException.StackTrace));
}
}
private static string GetWsClassName(string wsUrl)
{
string[] parts = wsUrl.Split('/');
string[] pps = parts[parts.Length - 1].Split('.');
return pps[0];
}
#endregion
}
}

浙公网安备 33010602011771号