如何使用AngularJs打造权限管理系统【简易型】


Posted in Javascript onMay 09, 2016

一、引言

本文将介绍如何把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只负责调用对应的应用层中的服务。这样设计好处有:

REST 服务部依赖与应用层接口,使得职责分离,将应用层服务的实例化交给单独的依赖注入容器去完成,而REST服务只负责调用对应应用服务的方法来获取数据。采用依赖接口而不依赖与具体类的实现,使得类与类之间低耦合。REST服务内不包括具体的业务逻辑实现。这样的设计可以使得服务更好地分离,如果你后期想用WCF来实现REST服务的,这样就不需要重复在WCF的REST服务类中重复写一篇Web API中的逻辑了,这时候完全可以调用应用服务的接口方法来实现WCF REST服务。所以将业务逻辑实现抽到应用服务层去实现,这样的设计将使得REST 服务职责更加单一,REST服务实现更容易扩展。

用户应用服务的实现:

public class UserService : BaseService, IUserService
 {
  private readonly IUserRepository _userRepository;
  private readonly IUserRoleRepository _userRoleRepository;
  public UserService(IUserRepository userRepository, IUserRoleRepository userRoleRepository)
  {
   _userRepository = userRepository;
   _userRoleRepository = userRoleRepository;
  }
  public GetResults<UserDto> GetUsers(PageInput input)
  {
   var result = GetDefault<GetResults<UserDto>>();
   var filterExp = BuildExpression(input);
   var query = _userRepository.Find(filterExp, user => user.Id, SortOrder.Descending, input.Current, input.Size);
   result.Total = _userRepository.Find(filterExp).Count();
   result.Data = query.Select(user => new UserDto()
   {
    Id = user.Id,
    CreateTime = user.CreationTime,
    Email = user.Email,
    State = user.State,
    Name = user.Name,
    RealName = user.RealName,
    Password = "*******",
    Roles = user.UserRoles.Take(4).Select(z => new BaseEntityDto()
    {
     Id = z.Role.Id,
     Name = z.Role.RoleName
    }).ToList(),
    TotalRole = user.UserRoles.Count()
   }).ToList();
   return result;
  }
  public UpdateResult UpdateUser(UserDto user)
  {
   var result = GetDefault<UpdateResult>();
   var existUser = _userRepository.FindSingle(u => u.Id == user.Id);
   if (existUser == null)
   {
    result.Message = "USER_NOT_EXIST";
    result.StateCode = 0x00303;
    return result;
   }
   if (IsHasSameName(existUser.Name, existUser.Id))
   {
    result.Message = "USER_NAME_HAS_EXIST";
    result.StateCode = 0x00302;
    return result;
   }
   existUser.RealName = user.RealName;
   existUser.Name = user.Name;
   existUser.State = user.State;
   existUser.Email = user.Email;
   _userRepository.Update(existUser);
   _userRepository.Commit();
   result.IsSaved = true;
   return result;
  }
  public CreateResult<int> AddUser(UserDto userDto)
  {
   var result = GetDefault<CreateResult<int>>();
   if (IsHasSameName(userDto.Name, userDto.Id))
   {
    result.Message = "USER_NAME_HAS_EXIST";
    result.StateCode = 0x00302;
    return result;
   }
   var user = new User()
   {
    CreationTime = DateTime.Now,
    Password = "",
    Email = userDto.Email,
    State = userDto.State,
    RealName = userDto.RealName,
    Name = userDto.Name
   };
   _userRepository.Add(user);
   _userRepository.Commit();
   result.Id = user.Id;
   result.IsCreated = true;
   return result;
  }
  public DeleteResult DeleteUser(int userId)
  {
   var result = GetDefault<DeleteResult>();
   var user = _userRepository.FindSingle(x => x.Id == userId);
   if (user != null)
   {
    _userRepository.Delete(user);
    _userRepository.Commit();
   }
   result.IsDeleted = true;
   return result;
  }
  public UpdateResult UpdatePwd(UserDto user)
  {
   var result = GetDefault<UpdateResult>();
   var userEntity =_userRepository.FindSingle(x => x.Id == user.Id);
   if (userEntity == null)
   {
    result.Message = string.Format("当前编辑的用户“{0}”已经不存在", user.Name);
    return result;
   }
   userEntity.Password = user.Password;
   _userRepository.Commit();
   result.IsSaved = true;
   return result;
  }
  public GetResult<UserDto> GetUser(int userId)
  {
   var result = GetDefault<GetResult<UserDto>>();
   var model = _userRepository.FindSingle(x => x.Id == userId);
   if (model == null)
   {
    result.Message = "USE_NOT_EXIST";
    result.StateCode = 0x00402;
    return result;
   }
   result.Data = new UserDto()
   {
    CreateTime = model.CreationTime,
    Email = model.Email,
    Id = model.Id,
    RealName = model.RealName,
    State = model.State,
    Name = model.Name,
    Password = "*******"
   };
   return result;
  }
  public UpdateResult UpdateRoles(UserDto user)
  {
   var result = GetDefault<UpdateResult>();
   var model = _userRepository.FindSingle(x => x.Id == user.Id);
   if (model == null)
   {
    result.Message = "USE_NOT_EXIST";
    result.StateCode = 0x00402;
    return result;
   }
   var list = model.UserRoles.ToList();
   if (user.Roles != null)
   {
    foreach (var item in user.Roles)
    {
     if (!list.Exists(x => x.Role.Id == item.Id))
     {
      _userRoleRepository.Add(new UserRole { RoleId = item.Id, UserId = model.Id });
     }
    }
    foreach (var item in list)
    {
     if (!user.Roles.Exists(x => x.Id == item.Id))
     {
      _userRoleRepository.Delete(item);
     }
    }
    _userRoleRepository.Commit();
    _userRepository.Commit();
   }
   result.IsSaved = true;
   return result;
  }
  public DeleteResult DeleteRole(int userId, int roleId)
  {
   var result = GetDefault<DeleteResult>();
   var model = _userRoleRepository.FindSingle(x => x.UserId == userId && x.RoleId == roleId);
   if (model != null)
   {
    _userRoleRepository.Delete(model);
    _userRoleRepository.Commit();
   }
   result.IsDeleted = true;
   return result;
  }
  public bool Exist(string username, string password)
  {
   return _userRepository.FindSingle(u => u.Name == username && u.Password == password) != null;
  }
  private bool IsHasSameName(string name, int userId)
  {
   return !string.IsNullOrWhiteSpace(name) && _userRepository.Find(u=>u.Name ==name && u.Id != userId).Any();
  }
  private Expression<Func<User, bool>> BuildExpression(PageInput pageInput)
  {
   Expression<Func<User, bool>> filterExp = user => true;
   if (string.IsNullOrWhiteSpace(pageInput.Name))
    return filterExp;
   switch (pageInput.Type)
   {
    case 0:
     filterExp = user => user.Name.Contains(pageInput.Name) || user.Email.Contains(pageInput.Name);
     break;
    case 1:
     filterExp = user => user.Name.Contains(pageInput.Name);
     break;
    case 2:
     filterExp = user => user.Email.Contains(pageInput.Name);
     break;
   }
   return filterExp;
  }
 }

这里应用服务层其实还可以进一步的优化,实现代码层级的读写分离,定义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前端的代码结构如下图所示:

如何使用AngularJs打造权限管理系统【简易型】

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打造权限管理系统【简易型】

六、总结

到此,本文的所有内容都介绍完了,尽管本文的AngularJS的应用项目还有很多完善的地方,例如没有缓冲的支持、没有实现读写分离,没有对一些API进行压力测试等。但AngularJS在实际项目中的应用基本是这样的,大家如果在项目中有需要用到AngularJS,正好你们公司的后台又是.NET的话,相信本文的分享可以是一个很好的参考。另外,关于架构的设计也可以参考我的另一个开源项目:OnlineStore和FastWorks。

以上所述是小编给大家介绍的使用AngularJs打造权限管理系统的方法,希望对大家有所帮助!

Javascript 相关文章推荐
默认让页面的第一个控件选中的javascript代码
Dec 26 Javascript
jQuery EasyUI API 中文文档 - Pagination分页
Sep 29 Javascript
javascript小数四舍五入多种方法实现
Dec 23 Javascript
js解析json读取List中的实体对象示例
Mar 11 Javascript
jquery如何扑捉回车键触发的事件
Apr 24 Javascript
javascript算法题:求任意一个1-9位不重复的N位数在该组合中的大小排列序号
Apr 01 Javascript
js实现touch移动触屏滑动事件
Apr 17 Javascript
JS匹配日期和时间的正则表达式示例
May 12 Javascript
如何利用ES6进行Promise封装总结
Feb 11 Javascript
javascript自定义日期比较函数用法示例
Jul 22 Javascript
element-plus一个vue3.xUI框架(element-ui的3.x 版初体验)
Dec 02 Vue.js
VUE解决跨域问题Access to XMLHttpRequest at
May 06 Vue.js
ajax跨域调用webservice的实现代码
May 09 #Javascript
MVC+jQuery.Ajax异步实现增删改查和分页
Dec 22 #Javascript
基于Jquery插件Uploadify实现实时显示进度条上传图片
Mar 26 #Javascript
jQuery实现三级菜单的代码
May 09 #Javascript
Boostrap入门准备之border box
May 09 #Javascript
CKEditor无法验证的解决方案(js验证+jQuery Validate验证)
May 09 #Javascript
jQuery代码实现图片墙自动+手动淡入淡出切换效果
May 09 #Javascript
You might like
Eclipse的PHP插件PHPEclipse安装和使用
2014/07/20 PHP
php中file_get_contents与curl性能比较分析
2014/11/08 PHP
PHP 以POST方式提交XML、获取XML,解析XML详解及实例
2016/10/26 PHP
jqeury eval将字符串转换json的方法
2011/01/20 Javascript
5分钟理解JavaScript中this用法分享
2013/11/09 Javascript
jquery中html、val与text三者属性取值的联系与区别介绍
2013/12/29 Javascript
jquery的clone方法应用于textarea和select的bug修复
2014/06/26 Javascript
js鼠标点击图片切换效果实现代码
2015/11/19 Javascript
JS三级可折叠菜单实现方法
2016/02/29 Javascript
js实现分割上传大文件
2016/03/09 Javascript
JS实现漂亮的时间选择框效果
2016/08/20 Javascript
微信小程序中button组件的边框设置的实例详解
2017/09/27 Javascript
关于HTTP传输中gzip压缩的秘密探索分析
2018/01/12 Javascript
vue实现百度搜索功能
2020/12/28 Javascript
微信小程序中的video视频实现 自定义播放按钮、封面图、视频封面上文案
2020/01/02 Javascript
微信小程序实现音频文件播放进度的实例代码
2020/03/02 Javascript
基于javascript处理二进制图片流过程详解
2020/06/08 Javascript
python读取注册表中值的方法
2013/04/08 Python
用python代码做configure文件
2014/07/20 Python
Django原生sql也能使用Paginator分页的示例代码
2017/11/15 Python
理论讲解python多进程并发编程
2018/02/09 Python
python plotly绘制直方图实例详解
2019/07/22 Python
Python中私有属性的定义方式
2020/03/05 Python
HTML5 Notification(桌面提醒)功能使用实例
2014/03/17 HTML / CSS
牧马人澳大利亚官网:Wrangler澳大利亚
2019/10/08 全球购物
微型企业创业投资计划书
2014/01/10 职场文书
拾金不昧表扬信范文
2014/01/11 职场文书
五水共治一句话承诺
2014/05/30 职场文书
2014年调度员工作总结
2014/11/19 职场文书
2014年社区民政工作总结
2014/12/02 职场文书
行政介绍信范文
2015/05/04 职场文书
小学五年级班主任工作经验交流材料
2015/11/02 职场文书
Python基于百度API识别并提取图片中文字
2021/06/27 Python
Python干货实战之八音符酱小游戏全过程详解
2021/10/24 Python
Python+OpenCV实现在图像上绘制矩形
2022/03/21 Python
MySQL数据库中的锁、解锁以及删除事务
2022/05/06 MySQL