美文网首页
Asp.net(二)业务处理接口项目(Web Api)

Asp.net(二)业务处理接口项目(Web Api)

作者: 未舞一刀 | 来源:发表于2016-11-21 15:14 被阅读0次

    简介

    Api作为业务逻辑提供方,承载了项目的核心逻辑,因而具有相对高的逻辑复杂性。在这样的前提下如何简化代码编写,如何规范统一书写风格和逻辑规范,如何提高代码的维护性和扩展性。项目的搭建的高内聚低耦合变得重要。
    示例的是一个企业级项目,框架图如下

    api层.jpg

    Security:重写了Http请求(Override DelegatingHandler),在请求的切面进行合法性判断,顺便进行签名要求的预处理。
    Client:定义了统一的接口调用方式共调用端使用,简化及统一了接口使用。
    Ctrl层:作为服务的直接提供方,在服务器上直接提供类似于RestFul风格的接口(感觉严格的RestFul风格,需要有完备的领域模型驱动,实际上的情况总是不尽如人意,领域抽象能力不够。),获取请求数据,按需调用Filter过滤器,进一步判断,调用
    Model层:作为业务模型层,提供业务逻辑的实际操作。使用统一的实体模型,并联系到Ibatis上,进行数据操作。
    具体的代码结构如下图:


    Api-UML.jpg

    下面是各个模块的详细介绍和代码示例:

    Entity library项目代码示例

    项目结构如下图:

    entity.jpg

    Domain模块,作为实体模型,简易代码如下

    public class User
    {
          public int Id { get; set; }
          public string NickName { get; set; }
          public string Avatar { get; set; }
    }
    

    Request,请求结构模型,利用了泛型接口,将请求类和返回类联系,起到了控制倒转的作用。

    public abstract class AbstractRequest
    {
        public bool ValidateParameters()
        {
            //公用方法示例,验证参数合法性
        }
    }
       public interface IRequest<T> where T:AbstractResponse
        {
            //获取接口名称
            string GetApiName();
    
            //获取接口编码
            string GetApiCode();
        }
    //获取User信息的请求结构定义
      public class GetUserRequest:AbstractRequest,IRequest<GetUserResponse>
        {
            public int Id { get; set; }
    
            public string GetApiName()
            {
                return "User.GetUserDetail";
            }
    
            public string GetApiCode()
            {
                return "User001";
            }
        }
    

    Response模块,作为请求的返回类型,定义统一的返回结构,便于消费者进行一致性返回码判断处理。

    public abstract class AbstractResponse
        {
            //返回码
            public int Code { get; set; }
            //报错信息
            public string Message { get; set; }
        }
     public class GetUserResponse:AbstractResponse
        {
            public User User { get; set; }
        }
    
    Service项目代码示例

    项目结构如下图:

    service.jpg

    代码示例:

     public interface IUserService
        {
            GetUserResponse GetUser(int id);
        }
     public class BaseService
        {
            //protected SqlInstance sqlInstance;
            public BaseService()
            {
                //sqlInstance=new SqlInstance(); //实例化数据库连接
                //...
            }
            //...
        }
      public class UserService:BaseService,IUserService
        {
            public GetUserResponse GetUser(int id)
            {
                //链接数据库获取数据
                //...
                throw new NotImplementedException();
            }
        }
    
    Security类库代码示例

    类库只是处理了安全性问题,在api请求入口处添加上权限判断。使用重写Http请求的方式。
    代码示例

    public class MyHandler : DelegatingHandler
        {
            protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            {
                IEnumerable<string> keyEnumerable;
                var t1 = request.Headers.TryGetValues("key", out keyEnumerable);
                var key = keyEnumerable.FirstOrDefault();
                if (!true)//验证类似于token的权限
                {
                    return await Task.Factory.StartNew<HttpResponseMessage>(
                                () => new HttpResponseMessage(HttpStatusCode.Forbidden)
                                {
                                    Content = new StringContent("error message")
                                });
                }
                //如果有signature,判断,并加结果标志,没有的话,清除signature相关信息,防止伪造。
                //.....
                return await base.SendAsync(request, cancellationToken);
            }
        }
    

    抽象出来的权限判断,可直接调用到webapi端,添加到路由配置代码中。

    WebApi项目示例

    作为接口的实际定义,webapi定义了接口文件的实际规则,并做出相应的安全管理及接口的权限控制。学习微信的权限控制,大概确定了几种接口:

    接口权限.png

    这些权限的判断都放在了Security做了集中管理。接口定义只需要在相应的逻辑上使用判断合法性即可。
    代码示例:

    public class UserController : ApiController
        {
            private IUserService userService;
    
            public UserController()
            {
                userService=new UserService();
            }
    
            [Signature]//安全签名过滤器判断
            [HttpPost]
            public GetUserResponse GetUser(GetUserRequest request)
            {
                //参数判断,安全性判断等等
                var ret = userService.GetUser(request.Id);
                return ret;
            }
    
        }
    

    以上是一个获取用户信息的示例接口,而作为接口入口的路由配置,则需要对请求的合法性进行判断,路由配置代码如下:

    public static void Register(HttpConfiguration config)
    {
                // Web API configuration and services
                // Configure Web API to use only bearer token authentication.
                config.SuppressDefaultHostAuthentication();
                config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
    
                // Web API routes
                config.MapHttpAttributeRoutes();
    
                config.Routes.MapHttpRoute(
                    name: "DefaultApi",
                    routeTemplate: "api/{controller}/{action}",
                    defaults: new { id = RouteParameter.Optional }
                );
                //添加的代码,添加http请求的入口处理
                config.MessageHandlers.Add(new MyHandler());
    }
    
    Client类库代码示例

    Client类库定义了接口调用的公共方法。
    1、利用泛型接口,将请求类和返回类进行了封装,简化调用的代码书写。
    2、并使得消费者调用接口需要通过代理类,避开了跨域的问题。
    3、消费者的调用都同意使用统一类库,是的日志的处理统一,返回的错误也可以进行一致化定义。
    代码示例如下:

     public interface IClient
     {
         T Execute<T>(IRequest<T> request) where T : AbstractResponse;
     }
    
    public class DefaultClient:IClient
        {
            private readonly string appKey;
            private readonly string appSecret;
            private readonly string baseUrl = "http://localhost:16469/api/";
            private readonly bool isNeedLogFile = false;
            private readonly LogFile logFile;
            public static readonly string SecureHeaderAppKey = "__secure_head_appkey__";
            public static readonly string SecureHeaderSignature = "__secure_head_signature__";
    
            public DefaultClient()
            {
                baseUrl = ConfigurationManager.AppSettings["service_base_url"];
                appKey = ConfigurationManager.AppSettings["app_key"];
                appSecret = ConfigurationManager.AppSettings["app_secret"];
                isNeedLogFile = "1".Equals(ConfigurationManager.AppSettings["client_log_file"]);
                logFile = new LogFile("client_log_path");
                logFile.SubPath = appKey;
            }
    
            public DefaultClient(string serviceBase, string code, string key)
            {
                baseUrl = serviceBase;
                appKey = code;
                appSecret = key;
            }
            public T Execute<T>(IRequest<T> request) where T : AbstractResponse
            {
                var webRequest = (HttpWebRequest)WebRequest.Create(baseUrl + request.GetApiName());
                webRequest.Method = "POST";
    
                string reqJson;
                string sign;
                using (Stream rs = webRequest.GetRequestStream())
                {
                    reqJson = JsonConvert.SerializeObject(request);
    
                    byte[] reqBytes = Encoding.UTF8.GetBytes(reqJson);
                    rs.Write(reqBytes, 0, reqBytes.Length);
                    rs.Close();
                }
    
                webRequest.ContentType = "application/json";
                webRequest.Headers.Add(SecureHeaderAppKey, appKey);
                sign = ComputeHash(appKey, appSecret, reqJson);
                webRequest.Headers.Add(SecureHeaderSignature, sign);
    
                //记录日志
                if (isNeedLogFile)
                {
                    logFile.Log(string.Format("[{0}] 请求内容: {1}", request.GetApiCode(), reqJson));
                    logFile.Log(string.Format("[{0}] 请求签名: {1}", request.GetApiCode(), sign));
                }
    
                try
                {
                    using (var resp = (HttpWebResponse)webRequest.GetResponse())
                    {
                        try
                        {
                            Stream respStream = resp.GetResponseStream();
    
                            if (respStream == null)
                            {
                                throw new WebException("GetResponseStream returned null");
                            }
                            var streamReader = new StreamReader(respStream);
                            string respStr = streamReader.ReadToEnd();
                            //记录日志
                            if (isNeedLogFile)
                            {
                                logFile.Log(string.Format("[{0}] 响应内容: {1}", request.GetApiCode(), respStr));
                            }
                            return JsonConvert.DeserializeObject<T>(respStr);
                        }
                        catch (Exception e)
                        {
                            //记录日志
                            if (isNeedLogFile)
                            {
                                logFile.Log(string.Format("[{0}] 响应错误: {1}", request.GetApiCode(), e.Message));
                            }
                            throw new ApplicationException(e.Message, e);
                        }
                    }
                }
                catch (WebException e)
                {
                    var errMsg = new StreamReader(e.Response.GetResponseStream()).ReadToEnd();
                    //记录日志
                    if (isNeedLogFile)
                    {
                        logFile.Log(string.Format("[{0}] 请求错误: {1}", request.GetApiCode(), errMsg));
                    }
                    throw new APIServiceException(errMsg);
                }
            }
            private string ComputeHash(string key, string secret, string body)
            {
                return
                    Convert.ToBase64String(
                        SHA1.Create().ComputeHash(Encoding.Default.GetBytes(string.Concat(key, secret, body.Trim()))));
            }
        }
    

    以上就是Api项目端的各个核心环节的详细介绍。
    接下来会对调用端即前端进行简单的介绍。

    相关文章

      网友评论

          本文标题:Asp.net(二)业务处理接口项目(Web Api)

          本文链接:https://www.haomeiwen.com/subject/qbgdpttx.html