不同系统利用Web Service交互项目中涉及到的一些知识点
最近在做一个项目,需要利用WebService进行不同系统间的交互,其中学习到了很多技术上的知识。很多东西还是会在真正的实践中发现问题,进而去解决的。接下去我会列举一些。
场景
系统A在外网上,系统B在内网,A需要发送消息到B,在B中根据发送的符合XML格式的raw字符串在B中做一些操作。因为A在外网,在A上enable了SSL,并且A的服务器上部署了一个类似proxy的webservice,只是做一些验证,真正的事情是在B中完成。于是也部署了一个webservice在A上。
1。数据结构只有出现在方法的参数,返回值上,才会在客户端生成代理类,这个代理类不包括原来数据类型中的方法。
原本有一个类型我想在A和B的webservice上都用,于是两个项目都引用了这个Model的DLL(A的webserive一个project,B
也一个,Model一个),但是编译会错,因为A会报这个类型在两个Namespace里都存在(因为A引用了B的webservice,A右引用了这个Model的DLL。会有一个类似的****.proxy.的namespace和一个dll自己的namespace)。于是不需要这个Model的DLL了,也可以移去这个project,A只要引用这个Webservice,自然生成了代理类。
方法并不会在代理类里生成,为了封装性的话可以通过partial类来解决这个问题。
2.IComparable
有时候我们一些数据类型会以集合的形式存在,比如List<MyType>,这个list需要根据Type李某一个字段排序的,我们可以让这个类型继承IComparable接口,如果从效率考虑可以继承范型的接口
class MyType:IComparable<MyType> { public int CompareTo(MyType) { return ******; } }
然后只要调用自己的Sort()方法就能自行排序了
3。Webservice对于有些参数类型,比如dictionary,hashtable等
只能自己定义类型满足类似的功能
[Serializable] public class SMSFKeyValuePair { public string Key { get; set; } public decimal Value { get; set; } }
4。如果你想通过浏览器测试,比如给QA一个测试页面。webservice中你要传递一个xml格式的字符串的,因为安全原因,runtime会block这种request
需要自己定一个custom validator
public class CustomRequestValidator : RequestValidator { //force web service accept xml string type parameter and skip the request validation protected override bool IsValidRequestString(HttpContext context, string value, RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex) { // Set a default value for the out parameter. validationFailureIndex = -1; return true; } }
同时配置webconfig
<httpRuntime requestValidationType="Mercer.SMSF.SMSFExternalWebSerivce.CustomRequestValidator.CustomRequestValidator" />
5。ASPNET自带IP lockdown的功能,只要在webconfig里面配置一下就行了
<system.webServer> <security> <ipSecurity allowUnlisted="false"> <clear/> <add ipAddress="127.0.0.1" allowed="true"/><!-- allow requests from the local machine --> <add ipAddress="123.200.169.186" allowed="true"/> <add ipAddress="220.157.70.222" allowed="true"/> </ipSecurity> </security> <modules runAllManagedModulesForAllRequests="true"/> </system.webServer>
6。Webservice的异步问题
A的webserivce的method1里面做了一些事情后会调用B的webservice的method2,而且method1需要在很短的时间里返回,但是method2又很费时,那怎么办?本来觉得只要在method1里面调用mehod2的异步的那个方法,method1就很快返回到client端了,经测试不可行。还是会等mthod2完成后返回,跟同步效果一样了。。。就单独一个webserive方法调用采用异步的那个方法应该能马上返回,因为这里有两层了,所以并不行。后来发现再method1里起一个线程去调用method2,能达到这个预期的效果。前段时间看了点.net的异步编程,于是试着用了下
//start a async task to generate result package Task.Factory.StartNew((o) => Method2(o), mytype) .ContinueWith((t) => { ExceptionManager.Publish(t.Exception); }, TaskContinuationOptions.OnlyOnFaulted);
7。xml 序列化
当我们需要返回给客户端一个xml格式的字符串的时候,我们该怎么做。我们可以定义一个类型,结构类似于xml。最后序列化这个对象就可以了。
那如何自定义这个类型的属性在生成的xml字符串里是node还是innertext还是attribute呢,还有如何自定义nodename,attributename呢。可以利用System.Xml.Serialization下很多attribute来定义
XmlRoot,XmlElement,XmlAttribute,XmlText,那些不需要序列化到xmlstring里的可以用XmlIgnore.
还有个问题有时需要一个CDATA类型的,可是.net框架目前没有支持这种类型怎么办,可以自己来
[Serializable] public class CDATA : IXmlSerializable { private string text; public CDATA() { } public CDATA(string text) { this.text = text; } public System.Xml.Schema.XmlSchema GetSchema() { return null; } public void ReadXml(System.Xml.XmlReader reader) { this.text = reader.ReadString(); } public void WriteXml(System.Xml.XmlWriter writer) { writer.WriteCData(this.text); } }
比如Comment字段可能需要序列化到CDATA里,先定一个Comment属性,还有个CDATAComment属性,序列化时ignore Comment这个属性
private string comment; [XmlIgnore] public string Comment { get { return comment; } set { comment = value; CDataComment = new CDATA(comment); } } [XmlElement(ElementName = "comment", Type = typeof(CDATA))] public CDATA CDataComment { get; set; }
然后我需要序列化这个类型的对象,我这里练了下扩展方法,而且因为这种xml序列化需求很多地方都要用到,我写了个范型的方法
public static class PackageExtension { public static string OutputXML<T> (this T instance) where T:IMyXmlSerializable { StringBuilder sb = new StringBuilder(); Type type = typeof(T); XmlSerializer serializer = new XmlSerializer(type); XmlSerializerNamespaces xmlns = new XmlSerializerNamespaces(); xmlns.Add(String.Empty, String.Empty); XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; using (XmlWriter xmlWriter = XmlWriter.Create(sb)) { serializer.Serialize(xmlWriter, instance, xmlns); } return sb.ToString(); } }
你可能会问IMyXmlSerializable是什么,此时只是一个标识,也就是说只有继承这个接口的类型才能调用这个扩展方法,不加这个限制的话,所有类都能调用了,自然会报错,于是只要在我需要xml序列化的类上标记继承这个接口就行了。这个接口其实是空的。
public interface IMyXmlSerializable { }
8。Webconfig protocols设置问题
VS跑起来webservice都能测试,可是部署上去不行,请看:
http://support.microsoft.com/kb/815150
http://msdn.microsoft.com/en-us/library/b2c0ew36%28v=vs.71%29.aspx
ms在本地加了个HttpPostLocalhost