zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    20 行代码:Serverless 架构下用 Python 轻松搞定图像分类
    Serverless 的内存配置与超时时间
    Serverless 架构与事件规范
    如何用 Serverless 优雅地给网站图片加水印
    修改rpmbuild构建目录的位置
    rpmbuild之构建目录结构解析
    全量编译与增量编译
    c堆排序的实现
    c优先队列的实现
    c栈的实现
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/5473403.html
Copyright © 2011-2022 走看看