如何使用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只負責呼叫對應的應用層中的服務。這樣設計好處有:
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前端的程式碼結構如下圖所示:
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的話,相信本文的分享可以是一個很好的參考。另外,關於架構的設計也可以參考我的另一個開源專案:onlinestore和fastworks。
以上所述是小編給大家介紹的使用angularjs打造權限管理系統的方法,希望對大家有幫助!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

JavaScript字符串替換方法詳解及常見問題解答 本文將探討兩種在JavaScript中替換字符串字符的方法:在JavaScript代碼內部替換和在網頁HTML內部替換。 在JavaScript代碼內部替換字符串 最直接的方法是使用replace()方法: str = str.replace("find","replace"); 該方法僅替換第一個匹配項。要替換所有匹配項,需使用正則表達式並添加全局標誌g: str = str.replace(/fi

簡單JavaScript函數用於檢查日期是否有效。 function isValidDate(s) { var bits = s.split('/'); var d = new Date(bits[2] '/' bits[1] '/' bits[0]); return !!(d && (d.getMonth() 1) == bits[1] && d.getDate() == Number(bits[0])); } //測試 var

本文探討如何使用 jQuery 獲取和設置 DOM 元素的內邊距和外邊距值,特別是元素外邊距和內邊距的具體位置。雖然可以使用 CSS 設置元素的內邊距和外邊距,但獲取準確的值可能會比較棘手。 // 設定 $("div.header").css("margin","10px"); $("div.header").css("padding","10px"); 你可能會認為這段代碼很

本文探討了十個特殊的jQuery選項卡和手風琴。 選項卡和手風琴之間的關鍵區別在於其內容面板的顯示和隱藏方式。讓我們深入研究這十個示例。 相關文章:10個jQuery選項卡插件

發現十個傑出的jQuery插件,以提升您的網站的活力和視覺吸引力!這個精選的收藏品提供了不同的功能,從圖像動畫到交互式畫廊。讓我們探索這些強大的工具:相關文章:1

HTTP-Console是一個節點模塊,可為您提供用於執行HTTP命令的命令行接口。不管您是否針對Web服務器,Web Serv

本教程向您展示瞭如何將自定義的Google搜索API集成到您的博客或網站中,提供了比標準WordPress主題搜索功能更精緻的搜索體驗。 令人驚訝的是簡單!您將能夠將搜索限制為Y

當div內容超出容器元素區域時,以下jQuery代碼片段可用於添加滾動條。 (無演示,請直接複製到Firebug中) //D = document //W = window //$ = jQuery var contentArea = $(this), wintop = contentArea.scrollTop(), docheight = $(D).height(), winheight = $(W).height(), divheight = $('#c
