【WCF】错误处理(三):错误协定
最近折腾换电脑的事,博客就更新慢了点。好,不废话,直入正题。
前面老周介绍过,SOAP消息中的错误信息是用一个 Fault 元素来包装的,前面老周也讲了其中的 FaultCode 元素,即可以对错误信息进行标识。并且也提到了,Fault 元素下的 faultstring 元素就是 FaultReason 所指定的内容。
今天咱们再了解另一个包装元素——detail。可以把 FaultReason 理解为对错误信息的文本概述,而 detail 元素则可以用于错误的详细信息,该元素下面你可以自由地放置XML元素,故灵活性也较强。
于是,detail 元素就与今天所讨论的错误协定扯上关系了。为啥,你想想啊,既然 detail 元素下面可以放XML元素,而数据协定在传输时默认也是使用XML序列化的,所以,所谓错误协定就是一个由开发者自定义的数据协定,可以更灵活地存储错误的详细信息,并且用 detail 元素包装。
理论方面的内容就是上面那点,老周不喜欢故弄玄虚,理论方面的东西都喜欢直接表述,你能看懂就行。
下面,来实例一下,这样你马上就能掌握了。
首先,定义服务协定,这个懂吧。
[ServiceContract(Namespace = "zhou-samples", Name = "demo")] public interface IDemo { [OperationContract(Action = "sqr")] double SQR(double input); }
老周打算让这个协定做平方运算,就是传入一个数值,然后返回其平方值,比如,传入3,就返回 3 * 3 = 9。
这个例子的重点是错误协定,其实它就是一个数据协定,我们用下面这个数据协定类来描述错误的详细信息。
[DataContract(Namespace = "zhou-data")] public class ErrorData { [DataMember] public string ArgName { get; set; } [DataMember] public double ErrorVal { get; set; } [DataMember] public string CalledAction { get; set; } }
随便写的,ArgName表示发生错误的参数名,ErrorVal表示出错的值,CalledAction则表示客户端正在调用的服务操要作的 Action 头。
那么,这个错误协定如何与服务协定关联,并且能被 WCF 运行时认识呢,很简单,在刚刚定义的服务协定的方法上,加上一个 FaultContract 特性,并把错误协定的类型关联的Type传递过去。所以,上面的协定代码可以这样改。
[ServiceContract(Namespace = "zhou-samples", Name = "demo")] public interface IDemo { [OperationContract(Action = "sqr")] [FaultContract(typeof(ErrorData))] double SQR(double input); }
这个特性只能用于方法,只能为每个服务操作设定,如果你想统一处理,可以实现 IErrorHandler 接口,这个老周后面再说。
接下来,我们就要实现服务类了。
internal class DemoService : IDemo { public double SQR(double input) { if (input <= 0d) { ErrorData erdata = new ErrorData { ArgName = nameof(input), ErrorVal = input, CalledAction = OperationContext.Current.IncomingMessageHeaders.Action }; throw new FaultException<ErrorData>(erdata, "参数必须大于0"); } return input * input; } }
在实现方法中,咱们来个判断,如果传入的值不是大于 0 的,就抛异常。注意,这里因为要用到前面定义的错误协定类型,所以,要用 FaultException 的一个派生类 —— FaultException<TDetail>。这个类只是加了一个泛型参数 TDetail,它就是你要用到的错误协定的类型,所以上面代码是这样抛异常的:
throw new FaultException<ErrorData>(erdata, "参数必须大于0");
ErrorData 就是刚刚上面我们定义的表示错误详细信息的数据协定类。
完事了,就是这样,easy 吧?
于是,在客户端试调用一下,而且传一个不正确的参数进去,并 catch 一下。
IDemo channel = factory.CreateChannel(); try { double r = channel.SQR(-1.3d); Console.WriteLine($"计算结果:{r}"); } catch(FaultException<ErrorData> fault) { string rs = fault.Reason.GetMatchingTranslation().Text; ErrorData data = fault.Detail; Console.WriteLine($"错误概要:{rs}"); Console.WriteLine($"发生错误的参数:{data.ArgName}\n错误的值:{data.ErrorVal}\n发生错误的操作:{data.CalledAction}"); }
catch 到异常后,咱们顺便输出到控制台窗口上,以方便观察。注意,FaultException<TDetail> 类的泛型参数可以通过 Detail 属性来获取。
运行这个示例后,服务器收到请求后检测出参数不对,于是抛出异常,错误信息将被封装成这样:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <s:Fault> <faultcode xmlns="">s:Client</faultcode> <faultstring xml:lang="zh-CN" xmlns="">参数必须大于0</faultstring> <detail xmlns=""> <ErrorData xmlns="zhou-data" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <ArgName>input</ArgName> <CalledAction>sqr</CalledAction> <ErrorVal>-1.3</ErrorVal> </ErrorData> </detail> </s:Fault> </s:Body> </s:Envelope>
其中,detail 元素中的XML片段为
<detail xmlns=""> <ErrorData xmlns="zhou-data" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <ArgName>input</ArgName> <CalledAction>sqr</CalledAction> <ErrorVal>-1.3</ErrorVal> </ErrorData> </detail>
这个其实就是把错误协定 XML 序列化后,再放到 detail 元素下。
希望这个如此简单的例子,能让大伙伴们了解错误协定是个啥玩意儿。
示例源代码下载:https://files.cnblogs.com/files/tcjiaan/wcfFaultDetailSample.zip
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战