本来想写这个帖子已经很久了,但是公司事情多,做着做着就忘记了。公司因为需要做接口,而且用的还是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) {
                        //处理数据
                    }
                }
             }
     });

 

至此,完毕!以上的通用接口处理器采用了模板方法设计模式,尽量减少重复工作量。

说明:本篇文章为作者原创,如需转载,请说明来源及出处!

 

 posted on 2017-11-09 16:04  F风  阅读(1346)  评论(0编辑  收藏  举报