本来想写这个帖子已经很久了,但是公司事情多,做着做着就忘记了。公司因为需要做接口,而且用的还是asp.net的老框架,使用Handler来做,没得办法,自己照着MVC写了一个通过的接口操作模板。
上送json数据,返回的也是json数据。可以像MVC一样自动绑定并可以进行DataAnnotations验证。尽量达到在业务逻辑处理区域不用对上送参数做过多的获取和判断,能一次搞定就一次搞定。
话不多说,上代码!!!
BaseClass:用作接口参数的基类。接口参数类型可以继承该类,也可以不继承,或自己定义其他基类。
public abstract class BaseClass { }
EmptyClass:一个空类。当做一个参数类型传入到接口中,占用地方而已。
public class EmptyClass : BaseClass { }
ModelError:错误消息载体类型。
[Serializable] public class ModelError { public ModelError(Exception exception) : this(exception, null) { } public ModelError(string errorMessage) { this.ErrorMessage = errorMessage ?? string.Empty; } public ModelError(Exception exception, string errorMessage) : this(errorMessage) { if (exception == null) throw new ArgumentNullException("exception"); this.Exception = exception; } public Exception Exception { get; private set; } public string ErrorMessage { get; private set; } }
ModelErrorCollection:错误消息载体集合。
[Serializable] public class ModelErrorCollection : Collection<ModelError> { public void Add(string errorMessage) { base.Add(new ModelError(errorMessage)); } public void Add(Exception exception) { base.Add(new ModelError(exception)); } }
ModelState:模型绑定状态。
/// <summary> /// 模型绑定状态 /// </summary> [Serializable] public class ModelState { private ModelErrorCollection _errors = new ModelErrorCollection(); public bool IsValid { get { return _errors.Count == 0; } } public ModelErrorCollection Errors { get { return _errors; } } }
ModelBinder:模型绑定抽象类,需要继承此抽象类。用来绑定上送参数并验证的基类。
/// <summary> /// 模型绑定抽象类,需要继承此抽象类。 /// </summary> /// <typeparam name="T"></typeparam> public abstract class ModelBinder<T> where T : class { protected ModelState _modelState; /// <summary> /// 模型绑定状态 /// </summary> public ModelState ModelState { get { if (_modelState == null) _modelState = new ModelState(); return _modelState; } } /// <summary> /// 绑定操作 /// </summary> /// <returns></returns> public abstract T Binder(); /// <summary> /// 验证实体数据合法性。如果有错误,请在ModelState参数中获取。 /// </summary> /// <param name="entity"></param> protected void Valide(object entity) { if (entity == null) return; //获取T类型的所有公共属性 Type type = entity.GetType(); PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); if (properties != null && properties.Count() > 0) { //针对每一个公共属性,获取其特性 foreach (var property in properties) { //如果当前属性为一个自定义类型 if (property.PropertyType != typeof(object) && Type.GetTypeCode(property.PropertyType) == TypeCode.Object) { this.Valide(property.GetValue(entity, null)); } else ValideProperty(entity, property); if (!_modelState.IsValid) break; } } } /// <summary> /// 验证属性的每一个特性约束 /// </summary> /// <param name="entity"></param> /// <param name="property"></param> private void ValideProperty(object entity, PropertyInfo property) { if (entity != null && property != null) { var attributes = property.GetCustomAttributes(typeof(ValidationAttribute), false); foreach (ValidationAttribute attribute in attributes) ValidatePropertyAttribute(entity, property, attribute); } } /// <summary> /// 使用特性对属性进行验证 /// </summary> /// <param name="entity"></param> /// <param name="property"></param> /// <param name="attribute"></param> private void ValidatePropertyAttribute(object entity, PropertyInfo property, ValidationAttribute attribute) { if (entity != null && property != null && attribute != null) { //找到该属性 //注明:每一个函数都应当具有独立性. PropertyInfo currentProperty = entity.GetType().GetProperties().Where(p => p.Name == property.Name).FirstOrDefault(); //判断当前特性是否有IsRequiredInstance字段,这是自定义的特性,用于验证同一个实例中两个不同共有属性的值 PropertyInfo[] pros = attribute.GetType().GetProperties(); if (pros.Where(it => it.Name == "IsRequiredInstance" && it.PropertyType == typeof(bool)).FirstOrDefault() != null) attribute.GetType().GetProperty("Instance").SetValue(attribute, entity, null); if (currentProperty != null) { var value = currentProperty.GetValue(entity, null); if (!attribute.IsValid(value)) _modelState.Errors.Add(attribute.ErrorMessage); } } } }
BaseHandler:一般处理逻辑的基类。
/// <summary> /// 一般处理逻辑的基类。 /// </summary> /// <typeparam name="T">请求参数模型</typeparam> /// <typeparam name="R">返回参数模型</typeparam> public abstract class BaseHandler<T, R> : ModelBinder<T> where T : class where R : class { protected readonly ILog log; public BaseHandler() { log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); } public BaseHandler(string typeName) { if ((typeName ?? "").Trim() != "") log = LogManager.GetLogger(typeName); else log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); } /// <summary> /// 处理接口API消息 /// </summary> /// <returns></returns> public abstract R Process(); /// <summary> /// 真正需要处理的接口逻辑 /// </summary> /// <param name="param">客户端传过来的请求参数</param> /// <returns></returns> protected abstract R DoWork(T param); }
IndexHandler:一般业务接口的模板方法处理器。该接口是基础性接口,如果有其他业务需求,请自行另外自定义或继承该接口。
/// <summary> /// 一般业务接口的模板方法处理器。 /// 该接口是基础性接口,如果有其他业务需求,请自行另外自定义或继承该接口。 /// </summary> /// <typeparam name="T"></typeparam> public abstract class IndexHandler<T> : BaseHandler<T, BaseResponseResult> where T : class { public IndexHandler() : base() { } public IndexHandler(string typeName) : base(typeName) { } /// <summary> /// 对实体模型进行绑定和参数的特性验证 /// </summary> /// <returns></returns> public override T Binder() { //初始化模型 T rc = default(T); try { //初始化ModelState if (_modelState == null) _modelState = new ModelState(); else _modelState.Errors.Clear(); //获取数据 Stream stream = HttpContext.Current.Request.InputStream; stream.Seek(0, SeekOrigin.Begin); byte[] buffer = new byte[stream.Length]; int count = stream.Read(buffer, 0, buffer.Length); if (count > 0) { string requestParam = Encoding.UTF8.GetString(buffer); //绑定数据 rc = new JavaScriptSerializer().Deserialize<T>(requestParam); if (rc != null) { //验证数据合法性 base.Valide(rc); } else _modelState.Errors.Add("绑定数据失败!"); } else _modelState.Errors.Add("请求参数为空!"); } catch (Exception ex) { _modelState.Errors.Add("绑定数据出现错误!"); } return rc; } /// <summary> /// 处理接口API消息 /// </summary> /// <returns></returns> public override BaseResponseResult Process() { BaseResponseResult rc = new BaseResponseResult(ErrorCode.OperationError); try { //绑定请求参数 T requestParam = Binder(); //开启逻辑操作 if (_modelState.IsValid) rc = DoWork(requestParam); else { StringBuilder sbuilder = new StringBuilder(); foreach (var error in _modelState.Errors) sbuilder.Append(error.ErrorMessage); rc.SetResult(ErrorCode.InvalideParameter, sbuilder.ToString()); } } catch (Exception ex) { rc.SetResult(ErrorCode.SystemError); rc.returnData = null; log.Error("Process", ex); } return rc; } }
BaseResponseResult:返回结果。
/// <summary> /// 返回结果 /// </summary> public class BaseResponseResult { public BaseResponseResult() { } public BaseResponseResult(int returnValue, string returnMsg) { _code = returnValue; _message = returnMsg; } public BaseResponseResult(ErrorCode code, string returnMsg="") { SetResult(code, returnMsg); } private int _code = 0; private string _message = ""; private string _contentType = "application/json"; /// <summary> /// 错误码,0表示成功,其他表示失败 /// </summary> public virtual int returnValue { get { return _code; } } /// <summary> /// 错误码,0表示成功,其他表示失败 /// </summary> public virtual string returnMsg { get { return _message; } } /// <summary> /// 返回的数据,json格式 /// </summary> public virtual object returnData { get; set; } /// <summary> /// 设置返回状态码 /// </summary> /// <param name="code"></param> public virtual void SetCode(int code) { _code = code; } /// <summary> /// 设置返回状态码 /// </summary> /// <param name="code"></param> public virtual void SetCode(ErrorCode code) { SetResult(code); } /// <summary> /// 设置返回消息 /// </summary> /// <param name="message"></param> public virtual void SetMessage(string message) { _message = message; } /// <summary> /// 设置返回状态码和消息 /// </summary> /// <param name="returnValue"></param> /// <param name="returnMsg"></param> public virtual void SetResult(int returnValue, string returnMsg) { _code = returnValue; _message = returnMsg; } /// <summary> /// /// </summary> /// <param name="code">ErrorCode代码</param> /// <param name="returnMsg">返回消息。如果此项不输入值,则自动设置默认值!</param> public virtual void SetResult(ErrorCode code, string returnMsg="") { this._code = (int)code; this._message = (returnMsg??"").Trim()!=""?returnMsg:ErrorMsg.ErrorMessage[code]; } /// <summary> /// 设置返回消息体 /// </summary> /// <param name="obj"></param> public virtual void SetReturnData(params object[] obj) { if (obj != null && obj.Length > 0) { this.returnData = obj.ToList(); } } public virtual void SetReturnData(object obj) { this.returnData = obj; } public virtual void SetContentType(string contentType) { this._contentType = contentType; } /// <summary> /// 将当前结果转化为JSON字符串 /// </summary> /// <returns></returns> public virtual string ToJson() { return new JavaScriptSerializer().Serialize(this); } public virtual void Response(bool isEnd = true) { HttpContext.Current.Response.ContentType = _contentType; HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*"); HttpContext.Current.Response.Write(this.ToJson()); if (isEnd) HttpContext.Current.Response.End(); } }
ErrorCode:返回状态码枚举。
public enum ErrorCode { /// <summary> /// 操作错误 /// </summary> OperationError = -1, /// <summary> /// 成功 /// </summary> Success = 0, /// <summary> /// 失败 /// </summary> Failed = 1, /// <summary> /// 无数据 /// </summary> NoData=2, /// <summary> /// 不存在此页面 /// </summary> NotExistPage=3, /// <summary> /// 无权限 /// </summary> NoPermission=4, /// <summary> /// 未登录 /// </summary> NoLogin=5, /// <summary> /// 被禁止 /// </summary> Forbidden=6, /// <summary> /// 请求参数格式不符合要求 /// </summary> InvalideParameter = 98, /// <summary> /// 无此接口 /// </summary> NoAction = 99, /// <summary> /// 系统错误 /// </summary> SystemError = 100, } public class ErrorMsg { public static Dictionary<ErrorCode, string> ErrorMessage = new Dictionary<ErrorCode, string> { { ErrorCode.OperationError,"服务器响应错误!"}, { ErrorCode.Success,"成功!"}, { ErrorCode.Failed, "失败!"}, { ErrorCode.NoData, "查无数据!"}, { ErrorCode.NotExistPage,"此页码不存在!"}, { ErrorCode.InvalideParameter,"请求参数非法!"}, { ErrorCode.NoAction,"无此接口!"}, { ErrorCode.SystemError,"系统错误!"}, { ErrorCode.NoPermission,"无权限操作此功能!" }, { ErrorCode.NoLogin,"未登录!"}, { ErrorCode.Forbidden,"操作被禁止!"}, }; }
接下来介绍使用方法:
IndexHandler_Base64:定义一个接收base64字符串参数的接口处理器。代码如下:
/// <summary> /// 一般业务接口的模板方法处理器,适用于参数为base64字符串的请求 /// </summary> /// <typeparam name="T"></typeparam> public abstract class IndexHandler_Base64<T> : IndexHandler<T> where T:class { //当前接口是否需要登录后才能操作 protected bool _isLoginRequired = false; /// <summary> /// 实例化一个只接收base64字符串的接口操作 /// </summary> /// <param name="isLoginRequired">当前接口是否需要登录。true:需要 false:不需要(默认)</param> public IndexHandler_Base64(bool isLoginRequired = false) : base() { _isLoginRequired = isLoginRequired; } /// <summary> /// 实例化一个只接收base64字符串的接口操作 /// </summary> /// <param name="typeName">发起日志记录的类名</param> /// <param name="isLoginRequired">当前接口是否需要登录。true:需要 false:不需要(默认)</param> public IndexHandler_Base64(string typeName,bool isLoginRequired=false) : base(typeName) { _isLoginRequired = isLoginRequired; } /// <summary> /// 对实体模型进行绑定和参数的特性验证 /// </summary> /// <returns></returns> public override T Binder() { //初始化模型 T rc = default(T); try { //初始化ModelState if (_modelState == null) _modelState = new ModelState(); else _modelState.Errors.Clear(); //获取数据 Stream stream = HttpContext.Current.Request.InputStream; stream.Seek(0, SeekOrigin.Begin); byte[] buffer = new byte[stream.Length]; int count = stream.Read(buffer, 0, buffer.Length); if (count > 0) { string requestParam = Encoding.UTF8.GetString(buffer); requestParam = Encoding.UTF8.GetString(Convert.FromBase64String(requestParam)); //绑定数据 rc = new JavaScriptSerializer().Deserialize<T>(requestParam); if (rc != null) { //验证数据合法性 base.Valide(rc); } else _modelState.Errors.Add("绑定数据失败!"); } else _modelState.Errors.Add("请求参数为空!"); } catch (Exception ex) { _modelState.Errors.Add("绑定数据出现错误!"); } return rc; } public override BaseResponseResult Process() { //如果该接口需要登录后才能操作,则检查当前用户是否登录 if (_isLoginRequired) { int userId = 0; try { userId = Login.GetSession("UserID"); } catch { } if (userId <= 0) return new BaseResponseResult(ErrorCode.NoLogin); } return base.Process(); } }
接下来定义一个Index.ashx文件,开始使用模板接口。
/// <summary> /// index 的摘要说明 /// </summary> public class Index : IHttpHandler { private readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); //当前登录的用户 public static int _thisUserId { get { return Login.GetSession("UserID"); } } public void ProcessRequest(HttpContext context) { BaseResponseResult rc = new BaseResponseResult(ErrorCode.OperationError, "获取数据失败!"); try { string action = GetAction(); switch (action) { //获取 xxx列表 case "1001": rc = new ListHandler().Process(); break; //获取 xxx信息 case "1002": rc = new ItemHandler().Process(); break; default: rc.SetResult(ErrorCode.NoAction); break; } } catch (Exception ex) { log.Error("ProcessRequest", ex); rc.SetResult(ErrorCode.SystemError); } rc.Response(); } public bool IsReusable { get { return false; } } /// <summary> /// 获取Action接口名称 /// </summary> /// <returns></returns> private string GetAction() { string rc = ""; try { rc = HttpContext.Current.Request["action"] ?? ""; if ((rc ?? "").Trim() == "") { Stream stream = HttpContext.Current.Request.InputStream; if (stream != null) { byte[] buffer = new byte[stream.Length]; int count = stream.Read(buffer, 0, buffer.Length); if (count > 0) { try { string requestParam = Encoding.UTF8.GetString(buffer); requestParam = Encoding.UTF8.GetString(Convert.FromBase64String(requestParam)); JsonData jd = JsonMapper.ToObject(requestParam); if (jd != null && jd.Keys.Contains("action")) rc = jd["action"].ToString(); } catch { } } } } } catch (Exception ex) { log.Error("GetAction", ex); } return rc; } } #region 接口处理区 #region 1001 获取 xxx列表 public class ListReqModel { [Required(ErrorMessage = "请上送需要类型!")] [Range(0,5, ErrorMessage = "type参数值有误!")] public int type { get; set; } /// <summary> /// 页码索引,从0开始 /// </summary> [Required(ErrorMessage = "请上送页码索引!")] [Min(0, ErrorMessage = "pageIndex参数值有误!")] public int pageIndex { get; set; } /// <summary> /// 每页个数 /// </summary> [Required(ErrorMessage = "请上送每页的个数!")] [Min(1, ErrorMessage = "pageSize参数值有误!")] public int pageSize { get; set; } } public class ListHandler : IndexHandler_Base64<ListReqModel> { public ListHandler() : base("ListHandler") { } protected override BaseResponseResult DoWork(ListReqModel param) { BaseResponseResult rc = new BaseResponseResult(ErrorCode.OperationError); int totalCount = 0; ProjectBLL biz = new ProjectBLL(); DataTable table = biz.GetList(param.type ,Index._thisUserId, out totalCount, param.pageIndex, param.pageSize); if (table != null && table.Rows.Count > 0) { rc.SetReturnData(new { //总个数 totalCount = totalCount, list = table.AsEnumerable().Select(it => new { id = it["ID"].ToInt64(), title = it["Title"].ToNormalString(), frontCover = it["FrontCover"].ToNormalString() }).ToList() }); rc.SetResult(ErrorCode.Success, "成功!"); } else if (totalCount > 0) rc.SetResult(ErrorCode.NotExistPage, "当前页码不存在!"); else rc.SetResult(ErrorCode.NoData, "没有数据哟!"); return rc; } } #endregion #region 1002 获取xxx信息 public class ItemReqModel { [Required(ErrorMessage = "请上送项目标识!")] [Min(1, ErrorMessage = "id参数值有误!")] public long id { get; set; } } public class ItemHandler : IndexHandler_Base64<ItemReqModel> { public ItemHandler() : base("ItemHandler") { } protected override BaseResponseResult DoWork(ItemReqModel param) { BaseResponseResult rc = new BaseResponseResult(ErrorCode.OperationError);return rc; } } #endregion#endregion
ajax的使用方式:
//接入说明: //1、请使用POST方式上传; //2、请将参数组装为Json字符串,如:{'action':'xxx','xx':'xx'}; //3、Json字符串组装好以后,将其变为Base64字符串后上传。 //上送数据案例如下所示: $.ajax({ type: "POST", dataType: "json", url: GetHost() + 'Index.ashx', data: Base.encode('{"action":"1001","type":"' + type + '","pgIndex":' + pageIndex + ',"pgSize":' + pageSize + '}'), contentType:"application/json", success: function (data) { //获取数据 if (data) { //成功获取数据 if (data.returnValue == 0) { //处理数据 } } } });
至此,完毕!以上的通用接口处理器采用了模板方法设计模式,尽量减少重复工作量。
说明:本篇文章为作者原创,如需转载,请说明来源及出处!