现在的位置: 主页 > 联系方式 > 文章列表

WebService 设计总结

作者:潜江市宏光畜牧有限公司 来源:www.qjhgnm.com 未知发布时间:2017-09-06 09:54:38
WebService 设计总结

接触过很多电商的WebService,有种一看就蛋疼的设计,今天要从这个反例说一说 WebService 的设计。

[WebMethod] public string QueryOrderDetail(string xml) { ... }
如上代码输入是一个XML,输出也是一个XML,方法内部自己在做序列化和反序列化。放着成熟的SOAP标准不用,自己再实现一套数据标准。
反而XML成为一个黑盒,调用双方不得不依赖于接口文档,真是吃力不讨好。

因此好的WebService接口,应该从下面几个方面仔细考虑:

一. 参数
(1) 参数应该直接使用简单的数据类型(POCO、POJO),甚至时间类型都可以考虑用string,只要双方约束好时间字符串的格式。
(2) 如果参数个数超过3个,那就需要考虑设计一个Class了,避免参数列表过长,当然这没有硬性规定。
(3) 设计统一的参数规则。比如对外提供的查询接口就要考虑分页相关的数据。保证类似的接口都有统一的参数定义,形成习惯是提升效率最好方式。
业务参数和非业务参数应该分开,比如分页的数据就可以抽象出基类。


二. 异常
(1) 使用框架中定义的Exception类型,比如:SoapException, FaultException(WCF)。
(2) 尽量避免将异常定义在返回值中,通过返回值定义错误那么无论服务端还是客户端都要写很多if ... else 分支。
(3) 系统异常和业务异常要区分好,比如使用 SoapException 可以用 Code 来区分,比如:System.Error 表示系统错误,Bussiness.Error 表示业务错误。
(4) 补充:.net framework 如果没有包装那么默认有两种 fautCode: soap:Client 和 soap:Server。假设客户端传入BadRequest 基本就是 soap:Client 错误,其他 没有自定义code的则就是 soap:Server 的错误。

三. 安全
无论何时都要保证系统的安全性,我觉得安全也分系统安全和业务安全两种:
(1) 系统安全主要是指客户端的认证授权,调用次数(需要考虑会不会拖垮业务系统) 等
(2) 业务安全主要是指数据查询/操作权限,站群系统,当然这个主要是从业务角度考虑的。


四. 日志
日志可以方便排查错误,还可以通过日志来分析服务基本信息(比如:调用次数,失败次数等),必要时还可以通过日志来进行重试。
另外要考虑开发的便捷,设计统一的日志拦截处理。

以 WebService Application (.NET 3.5) 为例,记录几种常用的编程技巧。
原始的 WebService 如下:

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Services; using WebService1.Entity; using WebService1.Service; using System.Web.Services.Protocols; namespace WebService1 { [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] [System.ComponentModel.ToolboxItem(false)] public class Service1 : System.Web.Services.WebService { [WebMethod] public PageResult QueryOrder(Query queryInfo) { OrderService service = new OrderService(); return service.Query(queryInfo); } } }

PageResult, Query 将统一的业务部分抽取出来,这样定义其他的业务对象就能简化了。

using System; using System.Collections.Generic; namespace WebService1.Entity { [Serializable] public class PageResult { public int PageNo { get; set; } public int PageSize { get; set; } public int TotalCount { get; set; } public int PageCount { get; set; } public bool HasNextPage { get; set; } public List Data { get; set; } } } using System; using System.Collections.Generic; namespace WebService1.Entity { [Serializable] public class Query { public int PageNo { get; set; } public int PageSize { get; set; } public T Condition { get; set; } } }
跳过业务处理部分,来关注一下应用框架考虑的日志和安全拦截。可以利用 .NET framework 的 Soap Extensions (msdn) 很容易地实现对 WebMethod 的 AOP。
Soap Extensions 可以通过两种方式“注入”: 自定义Atrribute 或者通过 Web.config 里的 soapExtensionTypes 进行声明。

TraceExtension 的实现:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.IO; using System.Web.Services.Protocols; using log4net; using System.Xml; namespace WebService1.Common { public class TraceExtension : SoapExtension { private ILog logger = LogManager.GetLogger(typeof(TraceExtension)); Stream oldStream; Stream newStream; public override System.IO.Stream ChainStream(System.IO.Stream stream) { oldStream = stream; newStream = new MemoryStream(); return newStream; } public override void ProcessMessage(SoapMessage message) { switch (message.Stage) { case SoapMessageStage.BeforeDeserialize: log4net.ThreadContext.Properties["ip"] = HttpContext.Current.Request.UserHostAddress; log4net.ThreadContext.Properties["action"] = message.Action; WriteInput(message); break; case SoapMessageStage.AfterDeserialize: break; case SoapMessageStage.BeforeSerialize: break; case SoapMessageStage.AfterSerialize: WriteOutput(message); break; default: throw new Exception("Invalid Stage"); } } public override object GetInitializer(Type serviceType) { return null; } public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attr) { return null; } public override void Initialize(object initializer) { //filename = (string)initializer; } public void WriteOutput(SoapMessage message) { string soapString = (message is SoapServerMessage) ? "SoapResponse" : "SoapRequest"; string content = GetContent(newStream); // 为了Format XML,如果从性能考虑应该去掉此处的处理 if (!string.IsNullOrEmpty(content)) { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(content); using (StringWriter sw = new StringWriter()) { using (XmlTextWriter xtw = new XmlTextWriter(sw)) { xtw.Formatting = Formatting.Indented; xmlDoc.WriteTo(xtw); content = sw.ToString(); } } } logger.Info(soapString + ":\n" + content); Copy(newStream, oldStream); } public void WriteInput(SoapMessage message) { Copy(oldStream, newStream); string soapString = (message is SoapServerMessage) ? "SoapRequest" : "SoapResponse"; string content = GetContent(newStream); logger.Info(soapString + ":\n" + content); } void Copy(Stream from, Stream to) { TextReader reader = new StreamReader(from); TextWriter writer = new StreamWriter(to); writer.WriteLine(reader.ReadToEnd()); writer.Flush(); } string GetContent(Stream stream) { stream.Position = 0; TextReader reader = new StreamReader(stream); string content = reader.ReadToEnd(); stream.Position = 0; return content; } } }TraceAttribute 实现如下:
using System; using System.Web.Services.Protocols; namespace WebService1.Common { [AttributeUsage(AttributeTargets.Method)] public class TraceAttribute : SoapExtensionAttribute { private int priority = 0; public override Type ExtensionType { get { return typeof(TraceExtension); } } public override int Priority { get { return priority; } set { priority = value; } } } }
其中 TraceExtension 利用 log4net 来记录调用 WebMethod 的Request 和 Response,还包括 ip 和 Action(Action其实对应的 WebMethod)
对应的 log4net 配置如下:

那么 WebMethod 只要加上 [Trace] 特性,就可以开启日志记录功能。
[WebMethod] [Trace] public PageResult QueryOrder(Query queryInfo) { OrderService service = new OrderService(); return service.Query(queryInfo); }

企业建站2800元起,携手武汉肥猫科技,做一个有见地的颜值派!更多优惠请戳:仙桃SEO http://xiantao.raoyu.net

上一篇:两个用SQL语句实现Discuz数据外部调用的实例 下一篇:最后一页