首頁  >  文章  >  後端開發  >  Asp.net(二)業務處理介面專案(Web Api)

Asp.net(二)業務處理介面專案(Web Api)

PHPz
PHPz原創
2017-04-04 15:32:553142瀏覽

簡介

Api作為業務邏輯提供方,承載了專案的核心邏輯,因而具有相對高的邏輯複雜性。在這樣的前提下如何簡化程式碼編寫,如何規範統一書寫風格和邏輯規範,如何提升程式碼的維護性和擴展性。專案的搭建的高內聚低耦合變得重要。
範例的是企業級項目,框架圖如下

Asp.net(二)業務處理介面專案(Web Api)

#api層.jpg

Security:重寫了Http請求(Override DelegatingHandler),在請求的切面進行合法性判斷,順便進行簽名要求的預處理。
Client:定義了統一的介面呼叫方式共呼叫端使用,簡化及統一了介面使用。
Ctrl層:作為服務的直接提供方,在伺服器上直接提供類似RestFul風格的介面(感覺嚴格的RestFul風格,需要有完整的領域模型##驅動# ,實際上的情況總是不盡人意,領域抽象能力不夠。層:作為業務模型層,提供業務邏輯的實際操作。使用統一的實體模型,並聯絡Ibatis上,進行資料操作。 具體的程式碼結構如下圖:


Api-UML.jpg

以下是各個模組的詳細介紹和程式碼範例:Asp.net(二)業務處理介面專案(Web Api)
Entity library專案程式碼範例

專案架構如下圖:

##entity.jpg

#Do

mainAsp.net(二)業務處理介面專案(Web Api)模組,作為實體模型,簡易程式碼如下

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();
        }
    }
Asp.net(二)業務處理介面專案(Web Api)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做了集中管理。介面定義只需要在對應的邏輯上使用判斷合法性即可。 Asp.net(二)業務處理介面專案(Web Api)程式碼範例:

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端展示

以上是Asp.net(二)業務處理介面專案(Web Api)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn