错误信息的一种定义方式

可恶的前言

话说最近开始写博客,折腾个人站点,突然也喜欢上了写点内容作为开发路上的一些记录。最近在开发公司的api,开发了几个之后,我意识到需要统一下错误信息,于是有了这篇文章,提交给小组review之后,觉得可行于是统一采取了这种处理方式。

 

 何为错误信息

说道错误信息,大家应该都很熟悉,例如windows的错误代码,html的错误代码(404,403等等)。在.net里,Exception大家都很熟悉,对于一个完整的、健壮的程序,Exception应该不在运行时随便的报给用户。试想用户随便点击下就弹出个.NET Framework Error什么的,人家对这个产品的影响一定大打折扣,甚至可以说这个根本不是一个完整的产品。所以,错误信息应该是对各种异常的高可读、高度统一的概括,用更友好的方式让用户知道这个调用出了什么问题,是没权限、参数错误或者是其他的错误,进而可以反馈给开发者,进行什么操作的时候提示了404,403等等。说了一堆废话之后我们来看看错误信息如何定义比较好?

 

定义错误信息

首先我们来看看一般的错误信息定义:

1. 一般时候会把错误信息定义在资源文件(Resource)里,这样Framework会帮我们转换成xml,并且提供了提示信息,但是这种方式不够灵活,能自定义处理的东西有限。

2. 另外的方式是定义成枚举或者伪枚举的类,这种类可以提供高可扩展性,自定义起来很方便。这边篇文章,我就是采用这种方式。

首先定义错误信息的属性,一般的错误信息,应该拥有ErrorCode(错误代码,例如:404)和ErrorMessage(错误信息)两个属性,如下:

 

 1 /// <summary>
 2 /// 错误信息类,注意错误信息可以附带的参数
 3 /// </summary>
 4 public class CustomerError
 5 {
 6 
 7     /// <summary>
 8     /// 错误代码
 9     /// </summary>
10     public int ErrorCode { get; set; }
11     /// <summary>
12     /// 错误信息
13     /// </summary>
14     public string ErrorMessage { get; set; }
15 }

 定义好了基本属性,我们来想错误信息一般都是固定的,不能改变的。所以我们应该把CustomerError改成值类型,如下:

 1 /// <summary>
 2 /// 错误信息类,注意错误信息可以附带的参数
 3 /// </summary>
 4 public class CustomerError
 5 {
 6     /// <summary>
 7     /// 错误代码
 8     /// </summary>
 9     public int ErrorCode { get; private set; }
10     /// <summary>
11     /// 错误信息
12     /// </summary>
13     public string ErrorMessage { get; private set; }
14 
15     public CustomerError(int code, string message)
16     {
17         this.ErrorCode = code;
18         this.ErrorMessage = message;
19     }   
20 }     

这里我把两个属性设置为私有的set,这样对外这两个属性就是只读的,并且定义一个两个参数的构造函数,这样CustomerError就必须要声明时就带上两个参数了,就是一个值类型对象。

然后,我们再来想想错误信息应该是预定义好的,程序一加载就全部加载进来了,最好像枚举类型一样。那么我们该怎么做呢?不知道大家是否对.NET里的Color对象有没有映像,起是Color对象和这里的Customer类很像,Color里面定义了很多颜色,就像枚举一样,可是起是它不是一个枚举,而是一个伪枚举类的实现。为了实现这样的效果,我们来进行修改,如下:

 1 /// <summary>
 2 /// 错误信息类,注意错误信息可以附带的参数
 3 /// </summary>
 4 public class CustomerError
 5 {
 6     /// <summary>
 7     /// 错误代码
 8     /// </summary>
 9     public int ErrorCode { get; private set; }
10     /// <summary>
11     /// 错误信息
12     /// </summary>
13     public string ErrorMessage { get; private set; }
14     /// <summary>
15     /// 错误信息参数
16     /// </summary>
17     public int ParamLength { get; private set; }
18     private CustomerError(int code, string message)
19     {
20         this.ErrorCode = code;
21         this.ErrorMessage = message;
22     }
23 
24     public static readonly CustomerError Success = new CustomerError(1, "操作成功");
25 
26     public static readonly CustomerError Failed = new CustomerError(0, "操作失败");
27 }

 上面把属性的set置为私有,并且把构造函数也置为私有,以便在类外无法实例化这个类。而内部定义static静态的错误信息属性供外部调用。这样一个伪枚举类就实现了,并且可以自定义这个类附带的属性。

 

升级改造

上面的错误信息类,看起来应该满足了需求,但是仔细想来还差很多,错误信息还应该附带自定义的参数。类似 参数param不合法,这样的指定参数信息。那么我们再来改造一下:

1 /// <summary>
2 /// 缺少参数{0}
3 /// </summary>
4 public static readonly CustomerError Error10001 = new CustomerError(10001, "缺少参数{0}");

 

这里我们定义了一个带参数的错误信息,调用方式如下:

//先定义一个Response基类里的错误信息
public class ResponseBase
{
    public int StateCode{get; set;}
    public string Message{get; set;}  
}

public object action(xxx request)
{
     var response = new ResponseBase{
        StateCode = CustomerError.Error10001.ErrorCode,
        Message = string.format(CustomerError.Error10001.ErrorMessage, "param")
    };
    
    return response;
}

 

上面的定义之后,可能看起来这个转换稍微有点繁琐,能不能来点更简单的呢?这时候飘过一个扩展方法,.NET里的神器啊,那咱们就拿他来扩展下CustomerError:

首先我们把CustomerError稍微扩展下,加个参数长度:

 1 /// <summary>
 2 /// 错误信息参数
 3 /// </summary>
 4 public int ParamLength { get; private set; }
 5 private CustomerError(int code, string message, int paramLength = 0)
 6 {
 7     this.ErrorCode = code;
 8     this.ErrorMessage = message;    
 9     this.ParamLength = paramLength;
10 }

然后呢,我们修改下CustomerError枚举的定义:

1  /// <summary>
2 /// 缺少参数{0}
3 /// </summary>
4 public static readonly CustomerError Error10001 = new CustomerError(10001, "缺少参数{0}", 1);

接着我们在类外实现一个扩展函数:

 1 public static class CustomerErrorExtends
 2 {
 3     /// <summary>
 4     /// 转换指定的错误为ResponseBase
 5     /// </summary>
 6     /// <param name="error">错误信息</param>
 7     /// <param name="param">错误信息参数</param>
 8     /// <returns></returns>
 9     public static ResponseBase ToResponse(this CustomerError error, params string[] param)
10     {
11         var msg = error.ErrorMessage;
12         if (param.Length == error.ParamLength)
13         msg = string.Format(error.ErrorMessage, param);
14         return new ResponseBase
15         {
16             State = error.ErrorCode,
17             ErrorMsg = msg
18         };
19     }
20 
21     /// <summary>
22     /// 转换指定的错误为ResponseBase
23     /// </summary>
24     /// <param name="error">错误信息</param>
25     /// <returns></returns>
26     public static ResponseBase ToResponse(this CustomerError error)
27     {
28         return error.ToResponse("");
29     }
30 }

一个漂亮的扩展函数就实现了,我们回到action看下如何改造:

1 public object action(xxx request)
2 {    
3     return CustomerError.Error10001.ToResponse("param");
4 }

是不是一下子就简单了很多了?

 

一个使用例子

 1 public object Get(DeleteCloudFileRequest request)
 2 {
 3     //检查id是否正确
 4     int fileId = 0;
 5     if (!int.TryParse(request.FileID, out fileId) || fileId <= 0)
 6         return CustomerError.Error10002.ToResponse("FileID");
 7 
 8     //检查文件是否存在
 9     var folder = FolderManager.GetFolder(fileId);
10     if (folder == null)
11         return CustomerError.Error10007.ToResponse();
12 
13     //检查当前用户 对该文件夹 是否有操作权限
14     if (!CheckUser(folder.CreatedID.ToString()))
15         return CustomerError.Error10003.ToResponse();
16 
17     //删除
18     try
19     {
20         FolderManager.DeleteCloudStorageFileByFolderID(fileId);
21     }
22     catch (Exception e)
23     {
24         //日志
25 
26         return CustomerError.Error10005.ToResponse();
27     }
28 
29     //操作成功
30     return CustomerError.Success.ToResponse();
31 }
View Code

 

Summary

上面就是我今天想到的一个错误信息类的定义过程,可能显得相当粗俗,在大拿面前班门弄斧了。希望大家看过之后觉得好的,点下推荐,觉得不好的请留言指教。我也希望不断改进我在代码中的不足。谢谢~

posted @ 2014-10-20 22:37  笋干  阅读(1390)  评论(2编辑  收藏  举报
求组队!team扩招中:戳这里