Asp.net(二)業務處理介面項目(Web Api)

來源:互聯網
上載者:User

簡介

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()))));        }    }
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.