1. Introduction
Cet article présentera comment appliquer AngularJS aux projets réels. Cet article utilisera AngularJS pour créer un système de gestion des autorisations simples. Je ne dirai pas beaucoup ci-dessous, allez simplement sur le sujet.
2. Introduction à la conception d'architecture globale
Tout d'abord, jetons un coup d'œil au schéma de conception architecturale de l'ensemble du projet:
D'après la figure ci-dessus, nous pouvons voir la structure globale de l'ensemble du projet. Ensuite, je présenterai en détail la structure globale du projet:
Utilisez ASP.NET Web API pour implémenter les services REST. Cette méthode de mise en œuvre a réalisé l'utilisation publique, le déploiement et une meilleure expansion des services back-end. La couche Web dépend de l'interface de service d'application et utilise Castle Windsor pour implémenter l'injection de dépendance.
Couche d'affichage (interface utilisateur)
La couche d'affichage utilise AngularJS pour implémenter des pages spa. Toutes les données de page sont chargées de manière asynchrone et actualisées localement, donc cette implémentation aura une meilleure expérience utilisateur.
Service de candidature
AngularJS demande à l'API Web d'obtenir des données via le service HTTP, et l'implémentation de l'API Web est d'appeler la couche d'application pour demander des données.
Couche d'infrastructure
La couche d'infrastructure comprend la mise en œuvre de l'entreposage et la mise en œuvre de certaines méthodes courantes.
L'implémentation de la couche d'entreposage est d'abord implémentée dans le code EF, et la méthode de migration EF est utilisée pour créer et mettre à jour la base de données.
La couche LH.Common implémente certaines méthodes courantes, telles que les classes d'aide au log, les extensions d'arborescence d'expression et d'autres classes.
Couche de domaine
La couche de domaine implémente principalement tous les modèles de domaine du projet, y compris la mise en œuvre du modèle de domaine et la définition de l'interface d'entreposage.
En plus d'introduire la structure complète, nous introduirons la mise en œuvre du service back-end et la mise en œuvre du Web frontal du projet respectivement.
3. Implémentation des services back-end
Les services backend utilisent principalement l'API Web ASP.NET pour implémenter les services backend, et Castle Windsor est utilisé pour compléter l'injection de dépendance.
Ici, nous utilisons la gestion des utilisateurs dans la gestion de l'autorisation pour introduire la mise en œuvre du service API Web REST.
Implémentation du service REST qui fournit des données utilisateur:
classe publique 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.GetUser (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}")] publicputBase 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); }}À partir de l'implémentation de code ci-dessus, on peut voir que le service de repos utilisateur dépend de l'interface avec iUserService et ne place pas toute logique métier dans l'implémentation de l'API Web de la manière traditionnelle, mais encapsule plutôt certaines implémentations commerciales spécifiques dans la couche d'application correspondante. L'API REST est uniquement responsable de l'appel des services dans la couche d'application correspondante. Ces avantages de conception comprennent:
Le service des services de repos s'appuie sur l'interface avec la couche d'application pour séparer les responsabilités et remettre l'instanciation du service de couche d'application à un conteneur d'injection de dépendance séparé pour l'achèvement. Le service REST est uniquement responsable d'appeler les méthodes de service d'application correspondantes pour obtenir des données. L'utilisation d'interfaces de dépendance au lieu d'implémentations avec des classes spécifiques fait un couplage faible entre les classes. Le service REST n'inclut pas les implémentations de logique métier spécifiques. Cette conception peut rendre les services mieux séparés. Si vous souhaitez utiliser WCF pour implémenter les services de repos au stade ultérieur, il n'est pas nécessaire d'écrire à plusieurs reprises une logique dans l'API Web dans la classe de service de repos de WCF. À l'heure actuelle, vous pouvez appeler la méthode d'interface du service d'application pour implémenter WCF REST Service. Par conséquent, l'implémentation de la logique métier est extraite à la couche de service d'application pour l'implémenter. Cette conception rendra les responsabilités de service de repos plus uniques et la mise en œuvre du service de repos plus facile à développer.
Implémentation des services d'application utilisateur:
classe publique UserService: BaserService, iUserService {private readonly iUserRepository _UserRepository; Private ReadOnly iUserroleRepository _UserRroleRopository; Public UserService (iUserRepository UserRepository, iUserRroleRopostory userRroleRepository) {_userRepository = userRepository; _UserRroleRepository = userRoleRepository; } public getResults <UserdTo> getUsers (PageInput Entrée) {var result = getDefault <GetResults <UserdTo >> (); var filterExp = buildExpression (entrée); 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, e-mail = user.email, state = user.state, name = user.name, realname = user.realname, mot de passe = "******", rôles = user.Userrols.take (4) .Select (z => New BasestityDto () {id = ° z.role.id, name = z.role.rolename}). Tolist (), totalRole = user.userroles.count ()}). Tolist (); Résultat de retour; } public updateResult UpdateUser (userdto user) {var result = getDefault <Medateresult> (); var exisuser = _UserRepository.findSingle (u => u.id == user.id); if (existUser == null) {result.message = "user_not_exist"; result.statecode = 0x00303; Résultat de retour; } if (isHassAmeName (existUser.name, exisuser.id)) {result.Message = "user_name_has_exist"; result.statecode = 0x00302; Résultat de retour; } existUser.realName = user.realName; existuser.name = user.name; exisuser.state = user.state; existuser.email = user.email; _UserRepository.update (exisUser); _UserRepository.Commit (); result.issaved = true; Résultat de retour; } public CreateResult <nt> addUser (userdto userdTo) {var result = getDefault <CreateResult <nt>> (); if (isHassameName (userdto.name, userdto.id)) {result.message = "user_name_has_exist"; result.statecode = 0x00302; Résultat de retour; } var user = new User () {CreationTime = DateTime.Now, mot de passe = "", email = userdto.email, state = userdto.state, realName = userdto.realName, name = userdto.name}; _UserRepository.add (utilisateur); _UserRepository.Commit (); result.id = user.id; result.iscreated = true; Résultat de retour; } public Deleteresult DeleteUser (int userId) {var result = getDefault <eleteresult> (); var user = _UserRepository.findSingle (x => x.id == utilisateur); if (user! = null) {_userRepository.delete (user); _UserRepository.Commit (); } result.isdeleted = true; Résultat de retour; } public updateresult updatepwd (userdto user) {var result = getDefault <Medateresult> (); var userentity = _UserRepository.findSingle (x => x.id == user.id); if (userentity == null) {result.message = string.format ("l'utilisateur actuellement édité" {0} "n'existe plus", user.name); Résultat de retour; } userentity.password = user.password; _UserRepository.Commit (); result.issaved = true; Résultat de retour; } public getResult <serddto> getUser (int userId) {var result = getDefault <GetResult <serddto >> (); var modèle = _UserRepository.findSingle (x => x.id == utilisateur); if (modèle == null) {result.message = "use_not_exist"; result.statecode = 0x00402; Résultat de retour; } result.data = new UserdTo () {CreateTime = Model.CreationTime, Email = Model.Email, id = Model.id, realName = Model.RealName, State = Model.State, Name = Model.Name, Password = "******"}; Résultat de retour; } public updateresult updateroles (userdto user) {var result = getDefault <Medateresult> (); var modèle = _UserRepository.findSingle (x => x.id == user.id); if (modèle == null) {result.message = "use_not_exist"; result.statecode = 0x00402; Résultat de retour; } var list = Model.Userroles.tolist (); if (user.roles! = null) {foreach (var item dans user.roles) {if (! list.exists (x => x.role.id == item.id)) {_UsererroleRepository.add (new userrole {roleId = item.id, userrid = model.id}); }} foreach (var item in list) {if (! user.roles.exists (x => x.id == item.id)) {_UserRroleRepository.Delete (item); }} _UserRroleRepository.Commit (); _UserRepository.Commit (); } result.issaved = true; Résultat de retour; } public Deleteresult Deleterole (int userId, int roleId) {var result = getDefault <eleteresult> (); var modèle = _UserroleRepository.findSingle (x => x.userid == userId && x.roleid == roleId); if (modèle! = null) {_UserroleRepository.Delete (modèle); _UserRroleRepository.Commit (); } result.isdeleted = true; Résultat de retour; } public bool exist (String username, String Motword) {return _userRepository.findSingle (u => u.Name == username && u.password == mot de passe)! = null; } private bool ishassameName (string name, int userId) {return! String.isnullorwhitespace (name) && _userRepository.find (u => u.Name == name && u.id! = userid) .any (); } Expression privée <func <utilisateur, 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); casser; Cas 1: filterExp = user => user.name.contains (pageInput.name); casser; Cas 2: filterExp = user => user.email.contains (pageInput.name); casser; } return filterExp; }}Ici, la couche de service d'application peut en fait être plus optimisée, implémenter la lecture au niveau du code et la séparation d'écriture, définir l'interface IreaDonlyService et l'interface iWriteServie, et abstraire les opérations d'écriture en base de base sous forme de méthodes génériques. Ces opérations d'ajout, de suppression et de modification sont publiques. La raison pour laquelle cette opération peut être publiée est que ces opérations sont très similaires, et elles ne sont rien de plus que les différentes entités des opérations. En fait, cette implémentation a été utilisée dans un autre projet open source: Onlinestore. Vous pouvez vous référer à cela pour l'implémenter vous-même.
Implémentation de la couche de stockage:
Les services d'application utilisateur ne comptent pas directement sur des classes d'entreposage spécifiques, mais comptent également sur leurs interfaces. La classe d'entreposage utilisateur correspondante est implémentée comme suit:
classe publique BaseRepository <Tenttity>: iResOsository <Tenttity> où Tentity: class, ientity {private readoLly ThreadLocal <SerManagerDBContext> _Localctx = new ThreadLocal <SerManagerDBContext> () => new UserManagerDBContext ()); public userManagerDBContext dbContext {get {return _localctx.value; }} public Tentity findSingle (expression <func <tentetity, bool >> exp = null) {return dbContext.set <tente> (). Asnotracking (). FirstOrdeFault (EXP); } public iQueryable <Tenttity> find (expression <func <tente, bool >> exp = null) {return filter (exp); } public IQueryable <Tenttity> trouver (expression <func <tentude, bool >> Expression, expression <func <tendity, dynamic>> sortpredicate, sortOrder sortOrder, int pageNumber, int pageSize) {if (pageNumber <= 0) lance un nouvel argumentofrangeException ("pageNumber", pagenumber, "paginumber doit être grand que oral pour."); si (pagesize <= 0) lance un nouvel argumentofRangeException ("pagesize", pagesize, "pagesize doit être grand que ou égal à 1."); var query = dbContext.set <Tenttity> (). Où (expression); var skip = (pageNumber - 1) * pagesize; var Take = pagesize; if (sortpredicate == null) lancez new invalidOperationException ("" Basé sur la requête de pagination doit spécifier les champs de tri et l'ordre de tri. "); switch (sortOrder) {case sortOrder.asceming: var pagedascengs = query.sortby (sortpredicate) .skip (skip) .take (prendre); retour pagedascerence; Case SORTORDE.DESCENCE: VAR PAGEDDESCENCE = QUERY.SORTBYDESCENCE (SortPredicate) .Skip (SKIP) .Take (Take); retour pagedDescending; } Jetez la nouvelle invalidOperationException ("Basé sur la requête de pagination doit spécifier les champs de tri et l'ordre de tri."); } public int getCount (expression <func <tente, bool >> exp = null) {return filter (exp) .Count (); } public void add (entité de tente) {dbContext.set <Tenttity> (). Add (entité); } public void Update (Tenty Entity) {dbContext.Entry (entité) .State = entityState.Modified; } public void Delete (Tenty Entity) {dbContext.Entry (entité) .State = entityState.deleted; DbContext.set <Tenttity> (). Supprimer (entité); } public void delete (icollection <Tenttity> entityCollection) {if (entityCollection.count == 0) return; DbContext.set <Tenttity> (). Attach (entityCollection.First ()); DbContext.set <Tenttity> (). Removerange (entityCollection); } Private iQueryable <Tenttity> filtre (expression <func <tente, bool >> exp) {var dbset = dbContext.set <tente> (). asqueryable (); if (exp! = null) dbset = dbset.where (exp); retour dbset; } public void commit () {dbContext.saveChanges (); }} classe publique UserRepository: BasErepository <User>, iUserRepository {}4. Implémentation frontale AngularJS
La mise en œuvre du Web frontal consiste à utiliser AngularJS pour l'implémenter et à adopter un modèle de développement modulaire. La structure de code spécifique du Web frontal est illustrée dans la figure ci-dessous:
APP / IMAGES // Store Ressources d'image utilisées par le Web Front-End App / Styles // Style Style Fichiers APP / SCRIPTS // Fichiers de script utilisés dans l'intégralité du répertoire de stockage du module de stockage du module de stockage du module // Directoire de stockage du module de stockage du module d'instructions AngularJ Application / modules // bibliothèque de dépendance du projet, angulaire, bootstrap, bibliothèque jQuery App / Views // Angularjs View Template Storage Directory
Le niveau d'appel et le backend entre les codes des applications Web développés à l'aide d'AngularJS sont essentiellement les mêmes que le backend, et ils sont également la page Affichage - Module Contrôleur - Service de service - Service API Web.
De plus, le chargement des ressources CSS et JS dans le Web frontal adopte la méthode du bundle pour réduire le nombre de ressources demandées, accélérant ainsi le temps de chargement de la page. Configuration de classe de bundle spécifique:
classe publique BundleConfig {// Pour plus d'informations sur le regroupement, visitez http://go.microsoft.com/fwlink/?linkid=301862 public static void RegisterBundles (BundleCollection Bundles) {// Class Library Dependency File Bundles.add (new ScriptBundle ("~ / / JS / Base / lib"). "~ / app / modules / jQuery-1.11.2.min.js", "~ / app / modules / angular / angular.min.js", "~ / app / modules / angular / angular-rete.min.js", "~ / app / modules / bootstrap / js / ui-bootstrap-tpls-0.13.0.Min.Js", "~ / app / modules / bootstrap-notify / bootstrap-sotify.min.js")); // AngularJS Project File bundles.add (new ScriptBundle ("~ / js / angularjs / app"). include ("~ / app / scripts / services / *. js", "~ / app / scripts / contrôlers / *. js", "~ / app / scripts / directives / *. "~ / app / scripts / app.js")); // Style Bundles.Add (new StyleBundle ("~ / JS / Base / Style"). Inclure ("~ / app / modules / bootstrap / css / bootstrap.min.css", "~ / app / styles / dashboard.css", "~ / app / styles / console.cs")); }}Home index.cshtml
<! Doctype html> <html ng-app = "lh"> <éadf> <meta name = "Viewport" contenu = "width = device-width" /> <itle> Système de gestion de l'autorisation simple démo </ title> @ styles.render ("~ / js / base / style") @ scripts.Render ("~ / js / bass Ng-Controller = "Navigation"> <Av> <div> <div> <Button Type = "Button" Data-Toggle = "ENCLAPSE" Data-Target = "# Navbar" Aria-Exed = "False" Aria-Controls = "Navbar"> <span> Togle Navigation </ Span> <pander> </ Span> <pan> </pande> href = "/"> gestion de l'autorisation simple systemdemo </a> </div> <div> <ul> <li ng-repeat = "item in ls"> <a href = "# {{item.urls [0] .link}}"> {{item.name}} </a> </li> </ul> </v> href = "@ url.action (" unglogin "," home ", null)"> {{Lang.exit}} </a> </div> </div> </av> <div> <div> <v> <ul> <li ng-repeat = "Item in urls"> <a href = "# {{item.link}}"> {{item.title}} </a> </li> </ul> </div> <div> <div ng-view> </div> </div> </div> </v> </ div> @ scripts.render ("~ / js / angularjs / app") </ body> </ html>5. Effet de fonctionnement
Après avoir introduit la mise en œuvre des annuels avant et arrière, jetons un coup d'œil à l'effet de fonctionnement de l'ensemble du projet:
6. Résumé
À ce stade, tous les contenus de cet article ont été introduits, bien que le projet d'application AngularJS dans cet article ait encore de nombreux domaines parfaits, tels que sans support tampon, pas de lecture et de séparation d'écriture, sans tests de contrainte sur certaines API, etc. Mais l'application des AngularJS dans les projets réels est essentiellement comme ceci. Si vous avez besoin d'utiliser AngularJS dans votre projet et que le backend de votre entreprise est .NET, je crois que le partage de cet article peut être une bonne référence. De plus, vous pouvez également vous référer à mon autre projet open source: Onlinestore et Fastworks pour la conception d'architecture.
Ce qui précède est la méthode d'utilisation d'AngularJS pour créer un système de gestion d'autorisation introduit par l'éditeur. J'espère que ce sera utile à tous!