Home  >  Article  >  Web Front-end  >  How to use AngularJs to build a permission management system [Simple]_AngularJS

How to use AngularJs to build a permission management system [Simple]_AngularJS

WBOY
WBOYOriginal
2016-05-16 09:00:212391browse

1. introduction

this article will introduce how to apply angularjs to actual projects. this article will use angularjs to build a simple permission management system. not much to say below, let’s go directly to the topic.

2. introduction to overall architecture design

first, take a look at the architectural design drawing of the entire project:

from the picture above, you can see the overall structure of the entire project. next, i will introduce the overall structure of the project in detail:

use asp.net web api to implement rest services. this implementation method has achieved common, separate deployment and better expansion of back-end services. the web layer relies on the application service interface and uses castle windsor to implement dependency injection.

display layer (user ui)

the display layer uses the spa page implemented by angularjs. all page data is loaded asynchronously and partially refreshed. this implementation will have a better user experience.

application service

angularjs requests web api to obtain data through http service, and the implementation of web api calls the application layer to request data.

infrastructure layer

the infrastructure layer includes the implementation of warehousing and the implementation of some public methods.

the warehouse layer is implemented using ef code first, and uses ef migration to create and update the database.

the lh.common layer implements some common methods, such as the implementation of log help classes, expression tree extensions and other classes.

domain layer

the domain layer mainly implements all domain models of the project, including the implementation of domain models and the definition of warehousing interfaces.

in addition to introducing the complete structure, the back-end service implementation and web front-end implementation of the project will be introduced separately.

3. back-end service implementation

back-end services mainly use asp.net web api to implement back-end services, and castle windsor is used to complete dependency injection.

here we take user management in permission management to introduce the implementation of rest web api service.

implementation of rest service that provides user data:

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);
  }
 }

from the above code implementation, it can be seen that the user rest service relies on the iuserservice interface, and does not put all business logic in the web api implementation in the traditional way, but encapsulates some specific business implementations into the corresponding in the application layer, rest api is only responsible for calling services in the corresponding application layer. the benefits of this design are:

rest service department relies on the application layer interface, which separates responsibilities and leaves the instantiation of application layer services to a separate dependency injection container to complete, while the rest service is only responsible for calling the methods of the corresponding application services to obtain data. the use of dependent interfaces rather than implementations of specific classes results in low coupling between classes. the rest service does not include specific business logic implementation. this kind of design can make services better separated. if you later want to use wcf to implement rest services, you don't need to repeat the logic in web api in wcf's rest service class. this is completely fine. call the interface method of the application service to implement the wcf rest service. therefore, the business logic implementation is extracted to the application service layer. this design will make the rest service responsibility more single and the rest service implementation easier to expand.

implementation of user application services:

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;
  }
 }

the application service layer here can actually be further optimized to realize the separation of reading and writing at the code level, define the ireadonlyservice interface and the iwriteservie interface, and abstract the write operation into baseservice using a generic method. such operations of adding, deleting, and modifying are shared. the reason why these operations can be shared is because these operations are very similar, but the entities of the operations are different. in fact, this kind of implementation has been used in another open source project of mine: onlinestore. you can refer to this to go there yourself accomplish.

implementation of warehousing layer:

user application services do not directly depend on specific storage classes, but also rely on their interfaces. the implementation of the corresponding user storage class is as follows:

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
 {
 }

4. angularjs front-end implementation

the web front-end is implemented using angularjs and adopts a modular development model. the specific code structure of the web front-end is shown in the figure below:

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视图模板存放目录

the calling hierarchy between the codes of web applications developed using angularjs is basically the same as that of the backend, which is also the view page -> controller module -> service module -> web api service.

and the web front-end css and js resources are loaded using the bundle method to reduce the number of resource requests, thereby speeding up page loading time. configuration of specific bundle class:

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"
     ));
  }
 }

home 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>

5. operation effect

after introducing the implementation of the front-end and back-end, let us take a look at the running effect of the entire project:

6. summary

at this point, all the content of this article has been introduced, although the angularjs application project in this article still has many improvements, such as no buffering support, no separation of reading and writing, and no stress testing of some apis. but the application of angularjs in actual projects is basically like this. if you need to use angularjs in your project, and your company's backend is .net, i believe the sharing of this article can be a good reference. in addition, regarding the design of the architecture, you can also refer to my other open source project: onlinestore and fastworks.

the above is the method introduced by the editor to create a permission management system using angularjs. i hope it will be helpful to everyone!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn