AngularJs打造一個簡易權限系統


AngularJs打造一個簡易權限系統

一、引言

  上一篇博文已經向大家介紹了AngularJS核心的一些知識點,在這篇博文將介紹如何把AngularJs應用到實際項目中。本篇博文將使用AngularJS來打造一個簡易的權限管理系統。下面不多說,直接進入主題。

二、整體架構設計介紹

  首先看下整個項目的架構設計圖:

  從上圖可以看出整個項目的一個整體結構,接下來,我來詳細介紹了項目的整體架構:

  采用Asp.net Web API來實現REST 服務。這樣的實現方式,已達到后端服務的公用、分別部署和更好地擴展。Web層依賴應用服務接口,並且使用Castle Windsor實現依賴注入。

  • 顯示層(用戶UI)

  顯示層采用了AngularJS來實現的SPA頁面。所有的頁面數據都是異步加載和局部刷新,這樣的實現將會有更好的用戶體驗。

  • 應用層(Application Service)

  AngularJS通過Http服務去請求Web API來獲得數據,而Web API的實現則是調用應用層來請求數據。

  • 基礎架構層

  基礎架構層包括倉儲的實現和一些公用方法的實現。

  倉儲層的實現采用EF Code First的方式來實現的,並使用EF Migration的方式來創建數據庫和更新數據庫。

  LH.Common層實現了一些公用的方法,如日志幫助類、表達式樹擴展等類的實現。

  • 領域層

  領域層主要實現了該項目的所有領域模型,其中包括領域模型的實現和倉儲接口的定義。

  介紹完整體結構外,接下來將分別介紹該項目的后端服務實現和Web前端的實現。

三、后端服務實現

   后端服務主要采用Asp.net Web API來實現后端服務,並且采用Castle Windsor來完成依賴注入。

  這里拿權限管理中的用戶管理來介紹Rest Web API服務的實現。

提供用戶數據的REST服務的實現:

復制代碼
 public class UserController : ApiController
    {
        private readonly IUserService _userService;

        public UserController(IUserService userService)
        {
            _userService = userService;
        }

        [HttpGet]
        [Route("api/user/GetUsers")]
        public OutputBase GetUsers([FromUri]PageInput input)
        {
            return _userService.GetUsers(input);
        }

        [HttpGet]
        [Route("api/user/UserInfo")]
        public OutputBase GetUserInfo(int id)
        {
            return _userService.GetUser(id);
        }

        [HttpPost]
        [Route("api/user/AddUser")]
        public OutputBase CreateUser([FromBody] UserDto userDto)
        {
            return _userService.AddUser(userDto);
        }

        [HttpPost]
        [Route("api/user/UpdateUser")]
        public OutputBase UpdateUser([FromBody] UserDto userDto)
        {
            return _userService.UpdateUser(userDto);
        }

        [HttpPost]
        [Route("api/user/UpdateRoles")]
        public OutputBase UpdateRoles([FromBody] UserDto userDto)
        {
            return _userService.UpdateRoles(userDto);
        }

        [HttpPost]
        [Route("api/user/DeleteUser/{id}")]
        public OutputBase DeleteUser(int id)
        {
            return _userService.DeleteUser(id);
        }

        [HttpPost]
        [Route("api/user/DeleteRole/{id}/{roleId}")]
        public OutputBase DeleteRole(int id, int roleId)
        {
            return _userService.DeleteRole(id, roleId);
        }
    }
復制代碼

  從上面代碼實現可以看出,User REST 服務依賴與IUserService接口,並且也沒有像傳統的方式將所有的業務邏輯放在Web API實現中,而是將具體的一些業務實現封裝到對應的應用層中,Rest API只負責調用對應的應用層中的服務。這樣設計好處有:

  1. REST 服務部依賴與應用層接口,使得職責分離,將應用層服務的實例化交給單獨的依賴注入容器去完成,而REST服務只負責調用對應應用服務的方法來獲取數據。采用依賴接口而不依賴與具體類的實現,使得類與類之間低耦合。
  2. REST服務內不包括具體的業務邏輯實現。這樣的設計可以使得服務更好地分離,如果你后期想用WCF來實現REST服務的,這樣就不需要重復在WCF的REST服務類中重復寫一篇Web API中的邏輯了,這時候完全可以調用應用服務的接口方法來實現WCF REST服務。所以將業務邏輯實現抽到應用服務層去實現,這樣的設計將使得REST 服務職責更加單一,REST服務實現更容易擴展。

  用戶應用服務的實現:

  View Code

  這里應用服務層其實還可以進一步的優化,實現代碼層級的讀寫分離,定義IReadOnlyService接口和IWriteServie接口,並且把寫操作可以采用泛型方法的方式抽象到BaseService中去實現。這樣一些增刪改操作實現公用,之所以可以將這里操作實現公用,是因為這些操作都是非常類似的,無非是操作的實體不一樣罷了。其實這樣的實現在我另一個開源項目中已經用到:OnlineStore.大家可以參考這個自行去實現。

  倉儲層的實現:

  用戶應用服務也沒有直接依賴與具體的倉儲類,同樣也是依賴其接口。對應的用戶倉儲類的實現如下:

復制代碼
public class BaseRepository<TEntity> : IRepository<TEntity>
        where TEntity :class , IEntity
    {
        private readonly ThreadLocal<UserManagerDBContext> _localCtx = new ThreadLocal<UserManagerDBContext>(() => new UserManagerDBContext());

        public UserManagerDBContext DbContext { get { return _localCtx.Value; } }

        public TEntity FindSingle(Expression<Func<TEntity, bool>> exp = null)
        {
            return DbContext.Set<TEntity>().AsNoTracking().FirstOrDefault(exp);
        }

        public IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> exp = null)
        {
            return Filter(exp);
        }

        public IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> expression, Expression<Func<TEntity, dynamic>> sortPredicate, SortOrder sortOrder, int pageNumber, int pageSize)
        {
            if (pageNumber <= 0)
                throw new ArgumentOutOfRangeException("pageNumber", pageNumber, "pageNumber must great than or equal to 1.");
            if (pageSize <= 0)
                throw new ArgumentOutOfRangeException("pageSize", pageSize, "pageSize must great than or equal to 1.");

            var query = DbContext.Set<TEntity>().Where(expression);
            var skip = (pageNumber - 1) * pageSize;
            var take = pageSize;
            if (sortPredicate == null)
                throw new InvalidOperationException("Based on the paging query must specify sorting fields and sort order.");

            switch (sortOrder)
            {
                case SortOrder.Ascending:
                    var pagedAscending = query.SortBy(sortPredicate).Skip(skip).Take(take);

                    return pagedAscending;
                case SortOrder.Descending:
                    var pagedDescending = query.SortByDescending(sortPredicate).Skip(skip).Take(take);
                    return pagedDescending;
            }

            throw new InvalidOperationException("Based on the paging query must specify sorting fields and sort order.");
        }

        public int GetCount(Expression<Func<TEntity, bool>> exp = null)
        {
            return Filter(exp).Count();
        }

        public void Add(TEntity entity)
        {
            DbContext.Set<TEntity>().Add(entity);
        }

        public void Update(TEntity entity)
        {
            DbContext.Entry(entity).State = EntityState.Modified;
        }

        public void Delete(TEntity entity)
        {
            DbContext.Entry(entity).State = EntityState.Deleted;
            DbContext.Set<TEntity>().Remove(entity);
        }

        public void Delete(ICollection<TEntity> entityCollection)
        {
            if(entityCollection.Count ==0)
                return;

            DbContext.Set<TEntity>().Attach(entityCollection.First());
            DbContext.Set<TEntity>().RemoveRange(entityCollection);
        }

        private IQueryable<TEntity> Filter(Expression<Func<TEntity, bool>> exp)
        {
            var dbSet = DbContext.Set<TEntity>().AsQueryable();
            if (exp != null)
                dbSet = dbSet.Where(exp);
            return dbSet;
        }

        public void Commit()
        {
            DbContext.SaveChanges();
        }
    }

public class UserRepository :BaseRepository<User>, IUserRepository
    {
         
    }
復制代碼

四、AngularJS前端實現

   Web前端的實現就是采用AngularJS來實現,並且采用模塊化開發模式。具體Web前端的代碼結構如下圖所示:

 

復制代碼
App/images // 存放Web前端使用的圖片資源

App/Styles // 存放樣式文件

App/scripts // 整個Web前端用到的腳本文件
                / Controllers // angularJS控制器模塊存放目錄
               /  directives // angularJs指令模塊存放目錄
              /   filters  // 過濾器模塊存放目錄
              /   services // 服務模塊存放目錄
            / app.js // Web前端程序配置模塊(路由配置)
App/Modules  // 項目依賴庫,angular、Bootstrap、Jquery庫

App/Views // AngularJs視圖模板存放目錄
復制代碼

  使用AngularJS開發的Web應用程序的代碼之間的調用層次和后端基本一致,也是視圖頁面——》控制器模塊——》服務模塊——》Web API服務。

  並且Web前端CSS和JS資源的加載采用了Bundle的方式來減少請求資源的次數,從而加快頁面加載時間。具體Bundle類的配置:

復制代碼
public class BundleConfig
    {
        // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862
        public static void RegisterBundles(BundleCollection bundles)
        {
            //類庫依賴文件
            bundles.Add(new ScriptBundle("~/js/base/lib").Include(
                    "~/app/modules/jquery-1.11.2.min.js",
                    "~/app/modules/angular/angular.min.js",
                    "~/app/modules/angular/angular-route.min.js",
                    "~/app/modules/bootstrap/js/ui-bootstrap-tpls-0.13.0.min.js",
                    "~/app/modules/bootstrap-notify/bootstrap-notify.min.js"
                   ));
            //angularjs 項目文件
            bundles.Add(new ScriptBundle("~/js/angularjs/app").Include(
                    "~/app/scripts/services/*.js",
                    "~/app/scripts/controllers/*.js",
                    "~/app/scripts/directives/*.js",
                    "~/app/scripts/filters/*.js",
                    "~/app/scripts/app.js"));
            //樣式
            bundles.Add(new StyleBundle("~/js/base/style").Include(
                    "~/app/modules/bootstrap/css/bootstrap.min.css",
                    "~/app/styles/dashboard.css",
                    "~/app/styles/console.css"
                    ));
        }
    }
復制代碼

   首頁 Index.cshtml

復制代碼
<!DOCTYPE html>
<html ng-app="LH">
<head>
    <meta name="viewport" content="width=device-width" />
    <title>簡易權限管理系統Demo</title>
    @Styles.Render("~/js/base/style")
    @Scripts.Render("~/js/base/lib")
</head>
<body ng-controller="navigation">
    <nav class="navbar navbar-inverse navbar-fixed-top">
        <div class="container-fluid">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="/">簡易權限管理系統Demo</a>
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav navbar-left">
                    <li class="{{item.isActive?'active':''}}" ng-repeat="item in ls">
                        <a href="#{{item.urls[0].link}}">{{item.name}}</a>
                    </li>
                </ul>
                <div class="navbar-form navbar-right">
                    <a href="@Url.Action("UnLogin", "Home", null)" class="btn btn-danger">
                        {{lang.exit}}
                    </a>
                </div>
            </div>
        </div>
    </nav>
    <div class="container-fluid">
        <div class="row">
            <div class="col-sm-3 col-md-2 sidebar">
                <ul class="nav nav-sidebar">
                    <li class="{{item.isActive?'active':''}}" ng-repeat="item in urls"><a href="#{{item.link}}">{{item.title}}</a></li>
                </ul>
            </div>
            <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
                <div ng-view></div>
            </div>
        </div>
    </div>
    @Scripts.Render("~/js/angularjs/app")
</body>
</html>
復制代碼

五、運行效果

   介紹完前后端的實現之后,接下來讓我們看下整個項目的運行效果:

六、總結

   到此,本文的所有內容都介紹完了,盡管本文的AngularJS的應用項目還有很多完善的地方,例如沒有緩沖的支持、沒有實現讀寫分離,沒有對一些API進行壓力測試等。但AngularJS在實際項目中的應用基本是這樣的,大家如果在項目中有需要用到AngularJS,正好你們公司的后台又是.NET的話,相信本文的分享可以是一個很好的參考。另外,關於架構的設計也可以參考我的另一個開源項目:OnlineStoreFastWorks

  本文所有源碼下載地址:PrivilegeManagement


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM