webservice接口使用方便,兼容性强,目前web服务很多采用这种方式
开发webservice接口也很简单,本文主要涉及webservice中xml的验证。
1.XSD数据验证
目前交流行的是xsd数据格式验证
一个简单的xsd文档
<?xml version="1.0" encoding="utf-8"?> <xs:schema id="XSDEmrNotes" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" > <xs:element name="EmrNotes"> <xs:complexType> <xs:sequence> <xs:element name="id" type="idT"/> <xs:element name="User" type="UserT"/> <xs:element name="pwd" type="pwdT"/> <xs:element name="Name" type="NameT"/> </xs:sequence> </xs:complexType> </xs:element> <xs:simpleType name="idT"> <xs:restriction base="xs:int"> <xs:pattern value="[0-9]{1,12}"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="UserT"> <xs:restriction base="xs:string"> <xs:maxLength value="100"/> <xs:minLength value="1"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="pwdT"> <xs:restriction base="xs:int"> <!--[0-9]{18}|[0-9]{17}X|[0-9]{17}x|[0-9]{17}|[0-9]{15}--> <xs:pattern value="[0-9]{1,18}"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="NameT"> <xs:restriction base="xs:string"> <xs:maxLength value="100"/> <xs:minLength value="1"/> </xs:restriction> </xs:simpleType>
pattern为限制条件,采用正则
对应的xml文档为
<?xml version="1.0" encoding="utf-8" ?> <EmrNotes > <id>123</id> <User>用户名</User> <pwd>1234567891</pwd>
<Name>姓名</Name>
</EmrNotes>
这里需要注意的是如果要命名空间的话,xml与xsd的命名空间需要一致,但是命名空间容易在以后的序列化中出现问题,所以全不要命名空间
二者之间需要匹配验证,为此写了一个验证类,这时构造函数:
/// <summary> /// 构造验证类 /// </summary> /// <param name="xmlString">xml实体</param> /// <param name="Sheet">表名</param> public ValidationWebModel(string xmlString, string Sheet) { dataSheet = Sheet; XmlSchemaSet schemaSet = new XmlSchemaSet(); schemaSet.Add(null, System.Web.HttpContext.Current.Server.MapPath("~/XXX/xml/XXX/" + dataSheet + "/" + dataSheet + ".xsd")); settings.ValidationEventHandler += new ValidationEventHandler(this.ValidationEventHandler); settings.ValidationType = ValidationType.Schema; settings.Schemas = schemaSet; xmlByte = Encoding.UTF8.GetBytes(xmlString); dataSheet = Sheet; }
验证的话使用xmlReader流过一遍,就像这样 while (xmlRead.Read()){}.早期也有人使用XmlSchemaCollection与XmlValidatingReader的方式来验证,也可以,但是目前MSDN上此方法已过时,所以不采用。
当然我们还需要一个ValidationEventHandler方法供XmlSchemaSet 作为过滤方法,所以定义如下
/// <summary> /// 验证传入的xml文档是否符合规范 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ValidationEventHandler(Object sender, ValidationEventArgs e) { if (e.Severity == XmlSeverityType.Warning) { // errorMassage = "插入成功." + e.Message; dataMessage = e.Message; } else if (e.Severity == XmlSeverityType.Error) { errorMassage = "插入失败," + e.Message; } }
整个类
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Web; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; using TeleStrokes; using UsersInfo; namespace TeleStrokesWeb.webService { public class ValidationWebModel { //业务操作类 EmrNotesBLL emrNotesBll=new EmrNotesBLL(); UsersBLL usersBll = new UsersBLL(); //EmrAcceBLL emrAcceBll; //xml设置规则 XmlReaderSettings settings = new XmlReaderSettings(); //数据表名 string dataSheet; //传入的字节 byte[] xmlByte; /// <summary> /// 要返回的错误信息 /// </summary> public string errorMassage { get; set; } /// <summary> /// 数据详情 /// </summary> public string dataMessage { get; set; } /// <summary> /// 构造验证类 /// </summary> /// <param name="xmlString">xml实体</param> /// <param name="Sheet">表名</param> public ValidationWebModel(string xmlString, string Sheet) { dataSheet = Sheet; XmlSchemaSet schemaSet = new XmlSchemaSet(); schemaSet.Add(null, System.Web.HttpContext.Current.Server.MapPath("~/Dictionary/xml/TeleStrokes/" + dataSheet + "/" + dataSheet + ".xsd")); settings.ValidationEventHandler += new ValidationEventHandler(this.ValidationEventHandler); settings.ValidationType = ValidationType.Schema; settings.Schemas = schemaSet; xmlByte = Encoding.UTF8.GetBytes(xmlString); dataSheet = Sheet; } /// <summary> /// 开始验证方法 /// </summary> /// <param name="dataSheet">要使用的数据表</param> /// <returns></returns> public T Check<T>()where T:class { using (MemoryStream ms = new MemoryStream(xmlByte)) { using (XmlReader xmlRead = XmlReader.Create(ms, settings)) { while (xmlRead.Read()) { } if (string.IsNullOrEmpty(errorMassage)) { ms.Position = 0; //验证成功,符合规范 ms.Position = 0; if (dataSheet == "EmrAcce") { //验证附件表 T result = SeralizeModel<T>(ms,CheckEmrNotesRealtion ); return result; } else if (dataSheet == "EmrNotes") { //验证病历表 Func<object, bool> notesValidate = CheckUserRelation; notesValidate += ValidateIp; notesValidate += CheckAddUser; T result = SeralizeModel<T>(ms, notesValidate); return result; } else { errorMassage = "传错表了"; return null; } // return SeralizeModel(ms, dataSheet); } else { return default(T); } } } } /// <summary> /// 序列化并且验证表实体 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="reader"></param> /// <param name="validate"></param> /// <returns></returns> private T SeralizeModel<T>(Stream reader,Func<object,bool> validate)where T:class { T newModel = XmlSerializer<T>(reader); validate(newModel); return newModel; } /// <summary> /// 序列化传入的流对象 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="stream"></param> /// <returns></returns> private T XmlSerializer<T>(Stream stream)where T:class { XmlSerializer ser = new XmlSerializer(typeof(T)); T tModel = ser.Deserialize(stream) as T ; return tModel; } /// <summary> /// 如果存在病人用户名验证是否在用户表中,返回警告信息 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="newEmrNote"></param> /// <returns></returns> private bool CheckUserRelation(object newEmrNote) { EmrNotes newEmrNotes = (EmrNotes)newEmrNote; if (!string.IsNullOrEmpty(newEmrNotes.EnUser)) { if (usersBll.GetUsersCheck(newEmrNotes.EnUser)) { return true;} else { dataMessage = "病人用户不是系统注册用户"; return false; } } return false; } /// <summary> /// 检查录入员是否是系统用户 /// </summary> /// <param name="newEmrNote"></param> /// <returns></returns> private bool CheckAddUser(object newEmrNote) { EmrNotes newEmrNotes = (EmrNotes)newEmrNote; if (!string.IsNullOrEmpty(newEmrNotes.EnAddUser)) { if (usersBll.GetUsersCheck(newEmrNotes.EnAddUser)) { return true; } else { dataMessage = "录入员不是系统注册用户"; return false; } } return false; } /// <summary> /// 检查传入的附件有无病历表中的相关项 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="newEmrAcce"></param> /// <returns></returns> private bool CheckEmrNotesRealtion(object newEmrAcce) { EmrAcce newModel = (EmrAcce)newEmrAcce; //验证是否是之前的数据关联 EmrNotes result = emrNotesBll.GetEmrNotes(newModel.Enid); if (result == null) { errorMassage = "没有相关病历的记录,请先传入病历表"; return false; } else { return true; } } /// <summary> /// 验证传入的xml文档是否符合规范 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ValidationEventHandler(Object sender, ValidationEventArgs e) { if (e.Severity == XmlSeverityType.Warning) { // errorMassage = "插入成功." + e.Message; dataMessage = e.Message; } else if (e.Severity == XmlSeverityType.Error) { errorMassage = "插入失败," + e.Message; } } /// <summary> /// 根据ip验证是否是用户本人 /// </summary> /// <param name="userName"></param> /// <returns></returns> private bool ValidateIp(object AcceNotes) { string userName=((EmrNotes)AcceNotes).EnAddUser; if (string.IsNullOrEmpty(userName)) return true; Users currentUser = usersBll.GetUsers(userName); if (currentUser!=null) { if (!string.IsNullOrEmpty(currentUser.URegIp)) { if (currentUser.URegIp != GetIp()) { //此处列为警告信息 // errorMassage = "录入员用户的ip地址不对"; dataMessage = "录入员用户的ip地址不对"; return false; } } } return true; } /// <summary> /// 获得iP地址 /// </summary> /// <returns></returns> private string GetIp() { string ip = ""; if (System.Web.HttpContext.Current.Request.ServerVariables["HTTP_VIA"] != null)// 服务器变量, using proxy { ip = System.Web.HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"].ToString(); } if (ip == "" || ip == null)//如果没有使用代理服务器或者得不到客户端的ip { //得到服务端的地址 ip = System.Web.HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"].ToString(); //While it can't get the Client IP, it will return proxy IP. if (ip == "" || ip == null)//如果没有使用代理服务器或者得不到客户端的ip not using proxy or cant get the Client IP { string strHostName = System.Net.Dns.GetHostName(); string clientIPAddress = System.Net.Dns.GetHostAddresses(strHostName).GetValue(0).ToString(); } if (ip == "" || ip == null) { ip = HttpContext.Current.Request.UserHostAddress; } } return ip; } } }
2.soapHeader验证
首先声明一个soapHeader的子类
public class SoapHeaderValidate:System.Web.Services.Protocols.SoapHeader { /// <summary> /// 用户名 /// </summary> public string UserName { get; set; } /// <summary> /// 密码 /// </summary> public string UserPwd { get; set; } /// <summary> /// 构造函数1 /// </summary> public SoapHeaderValidate() { } /// <summary> /// 构造函数2 /// </summary> /// <param name="UserName"></param> /// <param name="UserPwd"></param> public SoapHeaderValidate(string userName, string userPwd) { UserName = userName; UserPwd = userPwd; } }
然后在类中写一些验证方法
/// <summary> /// 主调方法:用于验证是否调用接口的用户存在 /// </summary> /// <param name="ErroMsg"></param> /// <returns></returns> public bool IsValid(out string ErroMsg) { ErroMsg = ""; try { if (string.IsNullOrEmpty(UserName) || string.IsNullOrEmpty(UserPwd)) { ErroMsg = "没有传递用户名"; return false; } UsersBLL userBll = new UsersBLL(); //暂定为测试有无用户名,以后改为状态或者登陆验证 if (!userBll.GetUsersCheck(UserName)) { ErroMsg = "用户名错误"; return false; } return true; } catch (Exception) { throw; } }
然后回到接口处在接口大类中声明了这个soapHeader后,添加如下标签
[SoapHeader("myHeader")] [WebMethod(Description = "测试soap连接方法,返回ok")] public string Hello() { string msg = string.Empty; if (myHeader.IsValid(out msg)) return msg; return "Ok"; }
接口写上
string msg = string.Empty; if (!myHeader.IsValid(out msg)) { return msg; }
ok,别人发送的信息就必须得通过头部验证了
客户端使用方式
SendData.SoapHeaderValidate myHeader = new SendData.SoapHeaderValidate(); myHeader.UserName = "bingren1";// myHeader.UserPwd = "111";
把这个声明的头加到web方法的首个参数就可以了