描述:
最近一个winform项目刚开发完成。采用c/s架构。闲来把一些技术点整理下了。
做项目之前调研了客户的电脑 .客户端机子性能一般,而且都是基于xp。
这些客观存在的问题,注定了实现过程中必须考虑客户端性能,frmaework版本等因素。
基于这些原因 我选择了2.0版本的 webservice。没有选择3.5版本的wcf,原因主要还是担心客户端环境的复杂度。使用2.0会保险点。(妈蛋,2.0写的各种蛋疼,没有语法糖很不爽)
细节:
1.首先肯定是要通过工具生成WSProxy.
2.webservice是基于请求request 和 返回 response来交互的。我将传输对象封装成实体,
RequestHead 对象是请求头,存放基础信息。
XObject 是传输的对象。原先是直接用object代替的。但是我们项目数据量比较大,所以对ojbect进行封装了,XObject在传输过程中主要是负责解析的。目前解析有三种形式:XML(ws标准), Json ,字节,目前我们采用的是json。
/// <summary> /// 请求参数 /// </summary> [GeneratedCode("System.Xml", "4.0.30319.18408")] [Serializable] [DebuggerStepThrough] [DesignerCategory("code")] [XmlType(Namespace = "http://tempuri.org/")] public class Request { private readonly RequestHead headField = new RequestHead(); private readonly XObject xbodyField = new XObject(); /// <summary> /// 请求的 头信息 /// </summary> public RequestHead Head { get { return headField; } set { headField.CopyFrom(value); } } /// <summary> /// 请求的 具体值 /// </summary> [XmlIgnore, SoapIgnore, JsonIgnore] public object Body { get { return xbodyField.Value; } set { xbodyField.Value = value; } } /// <summary> ////// </summary> public XObject XBody { get { return xbodyField; } set { xbodyField.CopyFrom(value); } } } /// <summary> /// 请求 头信息 /// </summary> [GeneratedCode("System.Xml", "4.0.30319.18408")] [Serializable] [DebuggerStepThrough] [DesignerCategory("code")] [XmlTypeAttribute(Namespace = "http://tempuri.org/")] public class RequestHead { public RequestHead() { RequestID = Guid.NewGuid().ToString().ToUpper(); } /// <summary> /// 用户ID /// </summary> //[XmlAttribute] public UserInfo User { get; set; } /// <summary> /// 请求GUID /// </summary> //[XmlAttribute] public string RequestID { get; set; } /// <summary> /// 请求的函数 /// </summary> //[XmlAttribute] public string Method { get; set; } /// <summary> /// 异步请求 /// </summary> //[XmlAttribute] public bool IsAsync { get; set; } /// <summary> /// 从 另外的对象 复制属性值 /// </summary> public void CopyFrom(RequestHead head) { //UserID = head == null ? string.Empty : head.UserID; RequestID = head == null ? string.Empty : head.RequestID; //ClientIP = head == null ? string.Empty : head.ClientIP; Method = head == null ? string.Empty : head.Method; IsAsync = head == null ? false : head.IsAsync; User = head == null ? null : head.User; } }
/// <summary> /// 响应结果 /// </summary> [GeneratedCode("System.Xml", "4.0.30319.18408")] [Serializable] [DebuggerStepThrough] [DesignerCategory("code")] [XmlTypeAttribute(Namespace = "http://tempuri.org/")] public class Response { private readonly ResponseHead headField = new ResponseHead(); private readonly XObject xbodyField = new XObject(); /// <summary> /// 响应的 头信息 /// </summary> public ResponseHead Head { get { return headField; } set { headField.CopyFrom(value); } } /// <summary> /// 响应的 具体值 /// </summary> [XmlIgnore, SoapIgnore, JsonIgnore] public object Body { get { return xbodyField.Value; } set { xbodyField.Value = value; } } /// <summary> ////// </summary> public XObject XBody { get { return xbodyField; } set { xbodyField.CopyFrom(value); } } public Response(){ } public Response(RequestHead requestHead) { this.Head.UserID = requestHead.User.UserID; this.Head.RequestID = requestHead.RequestID; } } /// <summary> /// 响应 头信息 /// </summary> [GeneratedCode("System.Xml", "4.0.30319.18408")] [Serializable] // [DebuggerStepThrough] [DesignerCategory("code")] [XmlTypeAttribute(Namespace = "http://tempuri.org/")] public class ResponseHead { public ResponseHead() { ResultCode = ResultCode.UnKown; } /// <summary> /// 请求版本 /// </summary> [XmlAttribute] public string Version { get; set; } /// <summary> /// 用户ID /// </summary> [XmlAttribute] public string UserID { get; set; } /// <summary> /// 请求GUID /// </summary> [XmlAttribute] public string RequestID { get; set; } /// <summary> /// 结果标志 /// </summary> [XmlAttribute] public ResultCode ResultCode { get; set; } /// <summary> /// 结果标志 /// </summary> [XmlAttribute] public string ResultNo { get; set; } /// <summary> /// 失败信息 /// </summary> [XmlAttribute] public string ResultMsg { get; set; } /// <summary> /// 时间戳 /// </summary> [XmlAttribute] public string Timestamp { get; set; } /// <summary> /// 利用请求给结果的头部信息赋值 /// </summary> /// <param name="header">请求头对象</param> public void SetHeaderInfo(RequestHead header) { this.RequestID = header.RequestID; this.UserID = header.User.UserID; } /// <summary> /// 设置结果信息 /// </summary> /// <param name="resultcode">结果类型</param> /// <param name="resultno">失败代号</param> /// <param name="resultmsg">失败信息</param> public void SetResult(ResultCode resultcode, long resultno, string resultmsg) { this.Timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fffff"); this.ResultCode = resultcode; this.ResultNo = resultno.ToString(); if (!string.IsNullOrEmpty(resultmsg)) this.ResultMsg = resultmsg; } /// <summary> /// 设置结果信息 /// </summary> /// <param name="resultcode">结果类型</param> /// <param name="resultno">失败代号</param> /// <param name="resultmsg">失败信息</param> public void SetFailResult(ResultCode resultcode, string resultno, string resultmsg) { this.Timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fffff"); this.ResultCode = resultcode; this.ResultNo = resultno; if (!string.IsNullOrEmpty(resultmsg)) this.ResultMsg = resultmsg; } /// <summary> /// 从 另外的对象 复制属性值 /// </summary> public void CopyFrom(ResponseHead head) { Version = head == null ? string.Empty : head.Version; UserID = head == null ? string.Empty : head.UserID; RequestID = head == null ? string.Empty : head.RequestID; ResultCode = head == null ? ResultCode.Fail : head.ResultCode; ResultNo = head == null ? string.Empty : head.ResultNo; ResultMsg = head == null ? string.Empty : head.ResultMsg; Timestamp = head == null ? string.Empty : head.Timestamp; } }
3.创建ws基类,利用模板模式能够很好的抽象出方法的共性。
/// <summary> /// WebService基类,所有继承者必须实现Request方法 /// </summary> [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [ToolboxItem(false)] public abstract class WSBase : WebService { ///// <summary> ///// IP白名单 ///// </summary> internal static string IPList = ""; /// <summary> /// WebService基类构造函数 /// </summary> static WSBase() { if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["IPList"].ToString())) IPList = ConfigurationManager.AppSettings["IPList"]; } /// <summary> /// 接收用户请求并返回相应结果 /// </summary> /// <param name="request">请求对象</param> [WebMethod(EnableSession = false)] public Response Request(Request request) { try { var response = new Response(); if (!Auth(request.Head)) { } //判断反序列化是否成功 if (request == null) { } //todo:安全校验iplist return RequestDeal(request,response); } catch (SoaException soaExp) { Log.Error(soaExp.Message,soaExp); response.Head.SetHeaderInfo(request.Head); response.Head.SetResult(ResultCode.Fail, soaExp.ResultNo, soaExp.Message); return response; } catch (Exception exp) { Log.Error(exp.Message, exp); response.Head.SetHeaderInfo(request.Head); response.Head.SetResult(ResultCode.Fail, 500, exp.Message); return response; } } /// <summary> /// 请求处理方法 /// </summary> protected abstract Response RequestDeal(Request request, Response response); /// <summary> /// 通过代理,获取客户端真实的IP地址 /// </summary> /// <returns>返回客户端真实的IP地址</returns> private static string GetClientIPAddress() {return ip; } /// <summary> /// 授权 /// </summary> /// <returns></returns> private bool Auth(RequestHead head) { return true; } }
实现类中实现 RequestDeal方法
[WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [System.ComponentModel.ToolboxItem(false)] // 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消注释以下行。 // [System.Web.Script.Services.ScriptService] public class TktService : WSBase { protected override Response RequestDeal(Request request,Response response) { try { object result = null; switch (request.Head.Method) { case "Np": new NpService().Np((HashRequest)request.Body, ref response); break; //case "GetSpByGuest": // new HjdService().GetByGuestCard((HashRequest)request.Body, ref response); // break; case "GetSpBy": //根据查询对象或者售票信息 new BuPiaoService().GetSpBy((BuPiaoQModel)request.XBody, ref response); break; } response.Head.SetHeaderInfo(request.Head); response.Head.Timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fffff"); } catch (Exception ex) { string logMsg = string.Format("ts服务出错: 函数名:{0},错误消息:{1}", request.Head.Method,ex.Message); response.Head.SetFailResult(ResultCode.Fail, "110", logMsg ); Log.Error(logMsg,ex); } return response; } }
4.客户端如何调用服务端方法
2.0环境下没有泛型,只能用object代替
public class WSClient { public static string CrsServiceURL = "CrsServiceURL"; public static string TktServiceURL = "TktServiceURL"; public static Response GetOData(string url, string method, object requestBody, UserInfo user) { WSProxy proxy = new WSProxy(); proxy.Url = WSProxy.GetWebServiceUrl(url); Request request = new Request(); request.Head.Method = method; request.Head.User = user; request.Body = requestBody; return proxy.Request(request); } public static object GetCrsData(string method, object requestBody, UserInfo user ) { var response =GetOData(OtherServiceURL, method, requestBody,user); //if (response.Head.ResultCode==ResultCode.Success) //{ return response.Body; } public static object GetTsData(string method, object requestBody, UserInfo user ) { var response = GetOData(TktServiceURL, method, requestBody,user);
return response.Body;
} }
5.OK基本就是这样。目前服务大概80多个,测试服务是非常耗时的。为了实现服务的本地调试,我们写了个类,针对开发过程中不启用服务直接调用方法的class。
项目过程中,前期对技术的难点、消耗的时间最好都做个有效的评估。尽量在前期把技术问题处理掉。