▄︻┻┳═一『异常捕获系列』Agenda:
▄︻┻┳═一有关于异常捕获点滴,plus我也揭揭java的短
▄︻┻┳═一根据异常自定义处理逻辑(【附】java异常处理规范)
▄︻┻┳═一利用自定义异常来重构代码
##先贴出来代码
项目中有个页面,用来支付回调。窗体里无任何html元素。服务端程序Page_Load如下,即获取到请求的订单,校验订单是否有效,然后持久化订单数据,并回写处理成功或失败的标识。
1 /// <summary> 2 /// 由PayAndRefund/RefundForm.aspx请求过来,处理业务系统的退款请求 3 /// </summary> 4 public partial class RefundForm : System.Web.UI.Page 5 { 6 private AlipayPaymentBLL.AlipayBLL obll = new AlipayPaymentBLL.AlipayBLL(); 7 private AlipayPaymentBLL.AlipayRefundRecord alipayRefundBll = new AlipayPaymentBLL.AlipayRefundRecord(); 8 protected void Page_Load(object sender, EventArgs e) 9 { 10 // 记录日志和相关标记 11 CommonUtils.LogCommon.instance.writePay(this, "==================================================================================="); 12 string sMsg = UrlInfo.Initial(Request).GetMsg(); 13 CommonUtils.LogCommon.instance.writePay(this, sMsg); 14 15 ReturnValue result; 16 try 17 { 18 string reqStr = WebCommon.ReadStreamReqStr(Request, System.Text.Encoding.UTF8);//获得流 序列化 19 var refundApply = JsonConvert.DeserializeObject<CommonModel.Domains.RefundApplyDTO>(reqStr); 20 if (refundApply == null) 21 { 22 result = new ReturnValue("9999", "无通知参数"); 23 } 24 else 25 { 26 var dt = new DataTable(); 27 var listOrderNo = new List<string>(); 28 var dicMoney = new Dictionary<string, string>(); 29 var dicRemark = new Dictionary<string, string>(); 30 result = alipayRefundBll.BuilderArray(listOrderNo, dicMoney, dicRemark, refundApply);//解析传入参数信息 31 if (result.Code == "0000") 32 { 33 result = new AlipayPaymentBLL.AlipayRefundRecord().CheckRefundRecordOrder(listOrderNo, refundApply); 34 if (result.Code == "0000") 35 { 36 result = alipayRefundBll.DoRefund(refundApply, dt, listOrderNo, dicMoney, dicRemark); 37 if (result.Code == "0000") 38 { 39 dt.Columns.Remove("trade_no"); 40 string[] sTableColumName = CommonModel.CommonFun.GetColumNameOfDataTable(dt); 41 // 将退款数据批量insert到dbo.T_AlipayRefundRecord 42 // 支付宝退款是有密的,这里只保存。 在单独的支付宝退款页做跳转到支付宝输入密码进行退款操作 43 bool bResult = alipayRefundBll.BulkCopy(dt, sTableColumName); 44 result = bResult ? new ReturnValue("0000", "支付宝退款请求支付中心已收到!") : new ReturnValue("9999", "退款信息入库失败!"); 45 } 46 } 47 } 48 } 49 } 50 catch (Exception ex) 51 { 52 CommonUtils.LogCommon.instance.writePay(this, "支付宝退款发起异常==>" + ex); 53 result = new ReturnValue("9999", "支付宝退款发起异常"); 54 } 55 56 string jsonStr = JsonConvert.SerializeObject(result); 57 CommonUtils.LogCommon.instance.writePay(this, "支付宝退款返回值==>" + jsonStr); 58 Response.Write(jsonStr); 59 } 60 }
##代码分析
不考虑逻辑,仅从代码简洁的角度来看,如上代码段存在如下问题,使得代码有了坏味道(bad smell):
* 对象oReturnValue被重复赋值并使用
* 层层嵌套, if和try...catch使得代码嵌套了好多层
##代码重构
从如下几个角度进行重构:
* 引入自定义异常, 当判断失败时,返回自定义异常。这样可以去掉很多if的嵌套。
* 要对捕获到的自定义异常做处理
重构后:
1 /// <summary> 2 /// 由PayAndRefund/RefundForm.aspx请求过来,处理业务系统的退款请求 3 /// </summary> 4 public partial class RefundForm : System.Web.UI.Page 5 { 6 private AlipayPaymentBLL.AlipayBLL obll = new AlipayPaymentBLL.AlipayBLL(); 7 private AlipayPaymentBLL.AlipayRefundRecord alipayRefundBll = new AlipayPaymentBLL.AlipayRefundRecord(); 8 protected void Page_Load(object sender, EventArgs e) 9 { 10 // 记录日志和相关标记 11 CommonUtils.LogCommon.instance.writePay(this, "==================================================================================="); 12 string sMsg = UrlInfo.Initial(Request).GetMsg(); 13 CommonUtils.LogCommon.instance.writePay(this, sMsg); 14 15 ReturnValue result; 16 try 17 { 18 string reqStr = WebCommon.ReadStreamReqStr(Request, System.Text.Encoding.UTF8);//获得流 序列化 19 var refundApply = JsonConvert.DeserializeObject<CommonModel.Domains.RefundApplyDTO>(reqStr); 20 if (refundApply == null) 21 { 22 throw new ResponseErrorException("无通知参数"); 23 } 24 25 if (alipayRefundBll.RefundOrderExists(refundApply.OrderNo)) 26 { 27 throw new ResponseErrorException("支付宝退款请求订单号:" + refundApply.OrderNo + "已提交支付中心,请不要重复提交!"); 28 } 29 30 var dt = alipayRefundBll.DoRefund1(refundApply); 31 dt.Columns.Remove("trade_no"); 32 // 将退款数据批量insert到dbo.T_AlipayRefundRecord 33 // 支付宝退款是有密的,这里只保存。 在单独的支付宝退款页做跳转到支付宝输入密码进行退款操作 34 bool saveSuccess = alipayRefundBll.BulkCopy(dt); 35 result = saveSuccess ? new ReturnValue("0000", "支付宝退款请求支付中心已收到!") : new ReturnValue("9999", "退款信息入库失败!"); 36 } 37 catch (Exception ex) 38 { 39 if (ex is ResponseErrorException) 40 { 41 result = new ReturnValue("9999", ex.Message); 42 } 43 else 44 { 45 CommonUtils.LogCommon.instance.writePay(this, "支付宝退款发起异常==>" + ex.ToString()); 46 result = new ReturnValue("9999", "支付宝退款发起异常"); 47 } 48 } 49 50 string jsonStr = JsonConvert.SerializeObject(result); 51 CommonUtils.LogCommon.instance.writePay(this, "支付宝退款返回值==>" + jsonStr); 52 Response.Write(jsonStr); 53 } 54 }
可见,代码清晰了很多。主要的方式是引入了自定义异常ResponseErrorException,使得方法只管返回理想情况下应该返回的参数类型,而现实很骨感,所以,当不满足判断条件时,就通过抛出自定义异常的方式来实现,同时也没有破坏方法的结构。 另外,我将异常捕获统一放到了主方法ProcessRequest里,也使得代码结构清晰,少了那些if的判断,是不是很漂亮?
public class ResponseErrorException : System.Exception { // // 摘要: // 使用指定的错误消息初始化 ResponseErrorException 类的新实例。 // // 参数: // message: // 描述错误的消息。 public ResponseErrorException(string message) : base(message) { } // // 摘要: 使用指定的错误消息初始化 ResponseErrorException 类的新实例 // // 参数: // format: // 复合格式字符串。 // // args: // 一个对象数组,其中包含零个或多个要设置格式的对象。 // // 异常: // System.ArgumentNullException: // format 或 args 为 null。 // // System.FormatException: // format 无效。- 或 -格式项的索引小于零或大于等于 args 数组的长度。 public ResponseErrorException(string format, params object[] args) : base(FormatString(format, args)) { } static string FormatString(string format, params object[] args) { try { string message = string.Format(format, args); return message; } catch (FormatException ex) { //LogHelper.Write("执行string.Format时(内容:"{0}",参数个数:{1})出现格式异常:{2}", format, args.Length, ex.Message); return string.Empty; } } }