1. 소개
이 기사는 실제 프로젝트에 AngularJS를 적용하는 방법을 소개합니다. 이 기사는 AngularJS를 사용하여 간단한 권한 관리 시스템을 만듭니다. 아래에서 많이 말하지 않고 주제로 가십시오.
2. 전체 아키텍처 설계 소개
먼저 전체 프로젝트의 건축 설계 다이어그램을 살펴 보겠습니다.
위 그림에서 전체 프로젝트의 전체 구조를 볼 수 있습니다. 다음으로 프로젝트의 전반적인 구조를 자세히 소개합니다.
ASP.NET Web API를 사용하여 휴식 서비스를 구현하십시오. 이 구현 방법은 백엔드 서비스의 대중 사용, 배포 및 더 나은 확장을 달성했습니다. 웹 계층은 응용 프로그램 서비스 인터페이스에 따라 다르며 Castle Windsor를 사용하여 종속성 주입을 구현합니다.
디스플레이 레이어 (사용자 UI)
디스플레이 레이어는 AngularJS를 사용하여 스파 페이지를 구현합니다. 모든 페이지 데이터는 비동기식으로로드되고 로컬로 새로 고침 되므로이 구현은 더 나은 사용자 경험을 제공합니다.
응용 프로그램 서비스
AngularJS는 Web API에 HTTP 서비스를 통해 데이터를 얻도록 요청하며 Web API의 구현은 응용 프로그램 계층을 호출하여 데이터를 요청하는 것입니다.
인프라 계층
인프라 계층에는 창고 구현 및 몇 가지 공통 방법의 구현이 포함됩니다.
창고 계층의 구현은 먼저 EF 코드로 구현되며 EF 마이그레이션 방법은 데이터베이스를 작성하고 업데이트하는 데 사용됩니다.
LH.common 레이어는 로그 도움말 클래스, 발현 트리 확장 및 기타 클래스와 같은 몇 가지 일반적인 방법을 구현합니다.
도메인 계층
도메인 계층은 주로 도메인 모델의 구현 및 창고 인터페이스의 정의를 포함하여 프로젝트의 모든 도메인 모델을 구현합니다.
완전한 구조를 도입하는 것 외에도 우리는 백엔드 서비스 구현과 프로젝트의 웹 프론트 엔드 구현을 각각 소개합니다.
3. 백엔드 서비스 구현
백엔드 서비스는 주로 ASP.NET 웹 API를 사용하여 백엔드 서비스를 구현하며 Castle Windsor는 종속성 주입을 완료하는 데 사용됩니다.
여기서 우리는 권한 관리에서 사용자 관리를 사용하여 REST Web API 서비스의 구현을 도입합니다.
사용자 데이터를 제공하는 REST 서비스 구현 :
공개 클래스 USERCONTROLLER : APICONTROLLER {private readOnly iuserservice _USERSERVICE; public usercontroller (iuserservice userervice) {_userservice = userervice; } [httpget] [Route ( "API/USER/GETUSERS")] public outputbase getUsers ([FromUri] pageinput input) {return _userService.getUsers (입력); } [httpget] [route ( "api/user/userinfo")] public outputbase getUserInfo (int id) {return _userService.getUser (id); } [httppost] [route ( "API/USER/ADDUSER")] public outputbase createUser ([from body] userDto userDTO) {return _USERSERVICE.ADDUSER (userDTO); } [httppost] [route ( "API/USER/UPDATEUSER")] public outputbase updateUser ([from body] userDto userDTO) {return _USERSERVICE.UPDATEUSER (userDTO); } [httppost] [route ( "API/USER/UPDATEROLES")] 공개 출력베이스 updateroles ([body] 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}/{roleiid}")] public outputbase deleterole (int id, int racenid) {return _userService.deleterole (id, roleiD); }}위의 코드 구현에서 사용자 REST 서비스는 IUSERSERVICE와의 인터페이스에 의존하며 Web API 구현에 모든 비즈니스 논리를 전통적인 방식으로 배치하지 않고 대신 특정 비즈니스 구현을 해당 애플리케이션 계층에 캡슐화 함을 알 수 있습니다. REST API는 해당 응용 프로그램 계층에서 서비스를 호출 할 책임이 있습니다. 이 설계 혜택은 다음과 같습니다.
REST Service Department는 응용 프로그램 계층과의 인터페이스에 의존하여 책임을 분리하고 응용 프로그램 계층 서비스의 인스턴스화를 개별 종속성 주입 컨테이너로 완료합니다. REST 서비스는 데이터를 얻기 위해 해당 응용 프로그램 서비스 방법을 호출 할 책임이 있습니다. 특정 클래스가있는 구현 대신 종속성 인터페이스를 사용하면 클래스간에 커플 링이 적습니다. REST 서비스에는 특정 비즈니스 로직 구현이 포함되어 있지 않습니다. 이 디자인은 서비스를 더 잘 분리 할 수 있습니다. WCF를 사용하여 이후 단계에서 REST 서비스를 구현하려면 WCF의 REST 서비스 클래스에서 웹 API에 논리를 반복적으로 작성할 필요가 없습니다. 현재 Application Service Interface 메소드를 호출하여 WCF REST 서비스를 구현할 수 있습니다. 따라서 비즈니스 로직 구현은 응용 프로그램 서비스 계층으로 추출하여이를 구현합니다. 이 디자인은 REST 서비스 책임을보다 단일 및 REST 서비스 구현을보다 쉽게 확장 할 수있게 해줍니다.
사용자 애플리케이션 서비스 구현 :
공개 클래스 사용자 서비스 : BaseService, iuserservice {private readonly iuserrepository _userrepository; 개인 Readonly iuserrolerepository _userrolerepository; public uservice (iuserrepository userrepository, iuserrolepository userrolerepository) {_userrepository = userrepository; _userrolerepository = userrolerepository; } public getResults <userdto> getUsers (pageInput input) {var result = getDefault <getResults <userDto >> (); var filterexp = buildExpression (입력); var query = _userRepository.find (filterExp, user => 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, ender = user.email, state = user.state, name = user.name, realname = user.realname, password = "******", roles = user.userroles.take (4). z.role.id, name = z.role.rolename}). tolist (), TotalRole = user.userroles.count ()}). tolist (); 반환 결과; } public updateResult updateUser (userDTO user) {var result = getDefault <uddateResult> (); var befortuser = _userRepository.findsingle (u => u.id == user.id); if (besctionser == null) {result.message = "user_not_exist"; result.statecode = 0x00303; 반환 결과; } if (ishassameName (exportUser.Name, Everser.Id)) {result.message = "user_name_has_exist"; result.statecode = 0x00302; 반환 결과; } cortedUser.RealName = user.RealName; besctions.name = user.name; Eversiter.state = user.state; ExportUser.email = user.email; _userRepository.update (besciper); _userRepository.commit (); result.issaved = true; 반환 결과; } 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; 반환 결과; } var user = new user () {creationtime = datetime.now, password = "", email = userdto.email, state = userdto.state, realname = userdto.realname, name = userdto.name}; _userRepository.add (사용자); _userRepository.commit (); result.id = user.id; result.iscreated = true; 반환 결과; } 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; 반환 결과; } public updateresult updatepwd (userdto user) {var result = getDefault <uddateResult> (); var userentity = _userrepository.findsingle (x => x.id == user.id); if (userentity == null) {result.message = string.format ( "현재 편집 된 사용자"{0} "더 이상 존재하지 않는다", user.name); 반환 결과; } userentity.password = user.password; _userRepository.commit (); result.issaved = true; 반환 결과; } 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; 반환 결과; } result.data = new userDto () {createTime = model.ceattime, email = model.email, id = model.id, realname = model.realName, state = model.state, name = model.name, password = "******"}; 반환 결과; } public updateresult updateroles (userdto user) {var result = getDefault <uddateResult> (); var model = _userrepository.findsingle (x => x.id == user.id); if (model == null) {result.message = "use_not_exist"; result.statecode = 0x00402; 반환 결과; } var list = model.userroles.tolist (); if (user.roles! = null) {foreach (user.roles의 var 항목) {if (! list.exists (x => x.role.id == item.id)) {_userrolerepository.add (new UserRole {reaconId = item.id, userId = model.id}); }} foreach (목록의 var 항목) {if (! user.roles.exists (x => x.id == item.id)) {_userrolerepository.delete (항목); }} _userrolerepository.commit (); _userRepository.commit (); } result.issaved = true; 반환 결과; } public deleteresult deleterole (int userId, int roleceid) {var result = getDefault <deleteresult> (); var model = _userrolerepository.findsingle (x => x.userid == userId && x.roleid == rolealID); if (model! = null) {_userrolerepository.delete (model); _userrolerepository.commit (); } result.isdeleted = true; 반환 결과; } public bool Events (문자열 사용자 이름, 문자열 암호) {return _userRepository.FindSingle (u => u.name == username && u.password == password)! = null; } private bool ishassamename (문자열 이름, 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)) filterexp를 반환합니다. switch (pageinput.type) {case 0 : filterexp = user => user.name.contains (pageinput.name) || user.email.contains (pageinput.name); 부서지다; 사례 1 : FilterExp = user => user.name.contains (pageinput.name); 부서지다; 사례 2 : FilterExp = user => user.email.contains (pageinput.name); 부서지다; } return filterexp; }}여기서, 응용 프로그램 서비스 계층은 실제로 최적화되고, 코드 레벨 읽기 및 쓰기 분리를 구현하고, ireadonlyservice 인터페이스와 iWriteserVie 인터페이스를 정의하고, 쓰기 작업을 일반적인 방법의 형태로 기본 서비스로 추상화 할 수 있습니다. 이러한 추가, 삭제 및 수정 작업은 공개적입니다. 이 작업이 공개 될 수있는 이유는 이러한 작업이 매우 유사하며 운영의 다른 엔티티에 지나지 않기 때문입니다. 실제로이 구현은 또 다른 오픈 소스 프로젝트 인 Onlinestore에서 사용되었습니다. 당신은 이것을 참조하여 직접 구현할 수 있습니다.
스토리지 계층 구현 :
사용자 애플리케이션 서비스는 특정 창고 클래스에 직접 의존하지 않고 인터페이스에 의존합니다. 해당 사용자 창고 클래스는 다음과 같이 구현됩니다.
공개 클래스 BaserePository <Tentity> : Irepository <Tentity> 여기서 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> (). } public iqueryable <tentity> find (expression <func <tentity, bool >> exp = null) {return filter (exp); } public iqueryable <tentity> find (표현 <func <tentity, bool >> 표현식, 표현, 표현 <func <tentity, dynamic >> sortorder, int pagenumber, int pagesize) {if (pagenumber <= 0) 새로운 ArgumentOutOfrangeException ( "pagenumber", pagenumber, "pagenumber는 1과 같거나 1과 동일해야합니다."); if (pagesize <= 0) 새로운 ArgumentOutOfRangeException ( "pagesize", pagesize, "pagesize는 1보다 큽니다"); var query = dbcontext.set <Tentity> (). 여기서 (표현); var skip = (pagenumber -1) * pagesize; var take = pagesize; if (sortpredicate == null) 새로운 InvalidOperationException 던지기 ( "페이징 쿼리를 기준으로 정렬 필드를 지정하고 순서를 정렬해야합니다."); switch (sortorder) {case sortorder.ascending : var pagedAscending = query.sortby (sortpredicate) .skip (skip) .take (take); PAGEDASCEND를 반환합니다. Case Sortorder.descending : var pagedDescending = Query.SortByDescending (SortPredicate) .skip (skip) .take (take); PAGEDDESCENDE를 반환합니다. } 새로운 InvalidOperationException을 던지십시오 ( "페이징 쿼리를 기준으로 정렬 필드를 지정하고 순서를 정렬해야합니다."); } public int getCount (expression <func <tentity, bool >> exp = null) {return filter (exp) .count (); } public void add (Tentity entity) {dbcontext.set <Tentity> (). add (엔티티); } 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> (). 제거 (엔티티); } public void delete (icollection <Tentity> entityCollection) {if (entityCollection.count == 0) return; dbcontext.set <Tentity> (). 첨부 (entityCollection.First ()); dbcontext.set <Tentity> (). Removerange (entityCollection); } private iqueryable <tentity> 필터 (Expression <func <tentity, bool >> exp) {var dbset = dbcontext.set <Tentity> (). AsQueryable (); if (exp! = null) dbset = dbset.where (exp); 반환 dbset; } public void commit () {dbcontext.sevechanges (); }} public class userrepository : baserepository <user>, iuserrepository {}4. AngularJS 프론트 엔드 구현
웹 프론트 엔드의 구현은 AngularJS를 사용하여이를 구현하고 모듈 식 개발 모델을 채택하는 것입니다. 웹 프론트 엔드의 특정 코드 구조는 다음과 같습니다.
App / Images // 웹 프론트 엔드 앱 / 스타일에서 사용하는 이미지 리소스 저장 // 스타일 파일 앱 / 스크립트 // 전체 웹 프론트 엔드 / 컨트롤러에 사용되는 스크립트 파일 // AngularJS 컨트롤러 모듈 스토리지 디렉토리 / 디렉토리 // AngularJS 명령 모듈 스토리지 디렉토리 // 필터 스토리지 디렉토리 / 서비스 // 서비스 모듈 디렉토리 (Web Pront-Progration // app). 구성) 앱/모듈 // 프로젝트 종속성 라이브러리, Angular, Bootstrap, JQuery Library 앱/뷰 // Angularjs보기 템플릿 스토리지 디렉토리
AngularJS를 사용하여 개발 된 웹 응용 프로그램 코드 간의 호출 레벨 및 백엔드는 기본적으로 백엔드와 동일하며 뷰 페이지 - 컨트롤러 모듈 - 서비스 모듈 - 웹 API 서비스이기도합니다.
또한 프론트 엔드 웹에서 CSS 및 JS 리소스의로드는 번들 방법을 채택하여 요청 된 리소스의 수를 줄여서 페이지로드 시간의 속도를 높입니다. 특정 번들 클래스 구성 :
공개 클래스 bundleconfig {// 번들링에 대한 자세한 내용은 http://go.microsoft.com/fwlink/?linkid=301862 Public Static void regis "~/app/modules/jquery-1.11.2.min.js", "~/app/modules/angular/angular.min.js", "~/app/modules/angular/angular-route.min.js", "~/app/modles/bootstrap/js/ui-bootstrap-tpls-0.13.0.js" "~/app/modules/bootstrap-notify/bootstrap-notify.min.js")); // Angularjs 프로젝트 파일 번들 .add (new ScriptBundle ( "~/js/angularjs/app"). 포함 ( "~/app/scripts/services/*. js", "~/app/scripts/controllers/*. "~/app/scripts/app.js"); // style bundles.add (new StyleBundle ( "~/js/base/style"). 포함 ( "~/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 = width = device-width"간단한 권한 관리 시스템 데모 </title> @styles.render ( "~/js/base/style") @scripts.render ( "~/js/lib") ng-controller = "navigation"> <avigation "> <avigation"> <avigation "div> <button type ="button "data-toggle ="collapse "data-target ="#navbar "aria-expanded ="false "aria-controls ="navbar "> <span> </span> <span> <span> <span> <span>. href = "/"> 간단한 권한 관리 시스템 데모 </a> </div> <div> <ul> <li ng-repeat = "ls"> <a href = "#{item.urls [0] .link}}"> {{item.name}} </a> </li> </ul> <a. href = "@url.action ("unlogin ","home ", null)"> {{lang.exit}} </a> </div> </div> </nav> <div> <div> <ul> <li ng-repeat = "urls in urls"> href = "#{{item.link}}"> {{item.title}} </a> </a> </li> </ul> </div> <div ng-view> </div> </div> </div> </div> @scripts.render ( "~/js/angularjs/app")5. 작동 효과
프론트 엔드의 구현을 도입 한 후 전체 프로젝트의 작동 효과를 살펴 보겠습니다.
6. 요약
이 기사의 모든 내용이 소개되었지만,이 기사의 AngularJS 애플리케이션 프로젝트에는 여전히 버퍼링 지원, 읽기 및 쓰기 분리, 일부 API에 대한 스트레스 테스트 등과 같은 많은 완벽한 영역이 있지만 실제 프로젝트에서 AngularJ를 적용하는 것은 기본적으로 다음과 같습니다. 프로젝트에서 AngularJS를 사용해야하고 회사의 백엔드가 .NET 인 경우이 기사의 공유는 좋은 참조가 될 수 있다고 생각합니다. 또한 건축 설계를위한 다른 오픈 소스 프로젝트 인 Onlinestore 및 Fastworks를 참조하십시오.
위의 것은 AngularJS를 사용하여 편집기가 소개 한 권한 관리 시스템을 작성하는 방법입니다. 나는 그것이 모두에게 도움이되기를 바랍니다!