描述:
最近一个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, , 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, "", 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。
项目过程中,前期对技术的难点、消耗的时间最好都做个有效的评估。尽量在前期把技术问题处理掉。