Application Web de zoo-blog
ASP NET.Core MVC Web App Utilisation de MSSQL EF6 Identité et Boostrap
Page du catalogue principal où vous pouvez faire défiler et choisir Animel pour explorer et commenter

À propos
Cette application Web ASP.Netcore présente le motif MVC avec une vue de mise en page contient une barre de navigation de navigation et un corps rendu avec des vues et des contrôleurs diff. J'ai inclus un composant de vue afin de garder les animels à explorer les divs plus communs entre les pages et stylisées à l'aide de Boostrap Libary
Modèle (Entity Framwork Core)
Diagramme MSSQL

Mon modèle contient 3 objets: catégorie, animal et commentaire. J'en ai donné à chacun plusieurs propulsités et des attributs de validation d'ajustement, y compris des modèles regex, des erreurs de Messege personnalisées de type de données, etc. J'ai créé deux attributs de vlidation personnalisés:
- La date de naissance pour valider l'animal est inférieure à 150 ans et est née le jour en cours ou plus tôt
- Validateur de fichier pour vérifier si le type de contenu du fichier inclut le mot "image" et la taille du fichier limité à 10 Mo
| classe publique ImageFileValidationAttribute : validationAttribute |
| { |
| const int max_file_size = 10 * 1024 * 1024 ; // 10 Mo |
| Validation de subsultation protégée ? IsValid ( objet ? Valeur , validationContext validationContext ) |
| { |
| if ( valeur est iformfile fichier ! = par défaut ) |
| { |
| if ( fichier . Longueur > max_file_size ) |
| retourner la nouvelle validationResult ( "La taille de ce fichier est plus grande que la limitation de 10 Mo" ) ; |
| if ( file . contentType . Contient ( "image" ) ) |
| Retour validationResult . Succès ; |
| return new ValidationResult ( "Ce n'est pas un fichier valide" ) ; |
| } |
| return new ValidationResult ( "Veuillez saisir un fichier image valide" ) ; |
| } |
| } |
Afin de générer les catégories, j'ai fabriqué un modèle ENUM HELPER qui n'est pas mappé dans la base de données, mais j'utilise pour générer une balise de sélection appropriée
Le projet de modèle contient également la couche d'accès aux données de base générique de base de repousse pour chaque entité dont l'ID est de type GUID et un service de formation d'image qui m'aide à enregistrer les fichiers d'images sous forme de tableau des octets et générer l'image du côté client
| octet statique public [ ] FormFiletobyteArray ( FormFile FormFile ) |
| { |
| if ( formFile ! = null ) |
| { |
| MemoryStream MemoryStream = new MemoryStream ( ) ; |
| FormFile . OpenReadStream ( ) . CopyTo ( MemoryStream ) ; |
| BYTE [ ] RAWDATA = MemoryStream . ToArray ( ) ; |
| Retour RawData ; |
| } |
| return par défaut ; |
| } |
| Public Static String FormatrawdatatoImage ( byte [ ] imagesfiledata ) |
| { |
| if ( imagesfiledata ! = null ) |
| Renvoie "Données: image; base64," + converti . Tobase64string ( imagesFileData ) ; |
| return par défaut ; |
| } |
| |
| } |
Voir
J'ai créé plusieurs vues pour les contrôleurs, un composant de vue et 3 vue partielle utile pour les styles de mise en page et les scripts et la barre de navigation La barre de navigation est utilisée pour naviguer entre les vues et actions différentes
La barre de navigation de l'application

La vue du gestionnaire de Create and Update contient une validation Vannila JS du type de fichier et de la taille pour empêcher le navigateur de lancer une erreur
| FileInput . addEventListener ( "change" , function ( ) { |
| Laissez FileSize = ceci . fichiers [ 0 ] . taille ; |
| if ( this . Files [ 0 ] === Undefined || fileSize === Undefined ) { |
| ce . setCustomValidity ( "Veuillez entrer le fichier" ) ; |
| ce . reportValidity ( ) ; |
| retour ; |
| } |
| if ( fileSize > maxFileSize ) { |
| ce . setCustomValidité ( "Ce fichier est supérieur à 10 Mo" ) ; |
| ce . value = "" ; |
| ce . reportValidity ( ) ; |
| retour ; |
| } |
| if ( ! ValidFileType ( this . Files [ 0 ] ) ) { |
| ce . setCustomValidité ( "Ce n'est pas un fichier d'image" ) ; |
| ce . value = "" ; |
| ce . reportValidity ( ) ; |
| retour ; |
| } |
| if ( fileSize < maxFileSize ) { |
| ce . setCustomValidité ( "" ) ; |
| ce . reportValidity ( ) ; |
| } |
| } ) ; |
Contrôleurs
Ce projet contient 4 contrôleurs:
- Accueil - Affichage des deux animaux les plus commentés
- Gestionnaire - Gestion de l'opération CRUD sur les données des animaux
- Catalogue - Consultez les animaux du blog et peut les trier par catégorie
- Données Animel - Explorez les détails des animaux et permettez à l'utilisateur de laisser un commentaire. La publication de commentaires utilise une API Fetch afin d'empêcher la relload de la page chaque fois que l'utilisateur publie un commentaire.
| Addcomment de fonction asynchrone ( événement ) { |
| Laissez comment = { |
| CommentID : indéfini , |
| Contenu : ContentTextArea . valeur , |
| Animelid : id , |
| } |
| commentaire = JSON . Stringify ( commentaire ) ; |
| attendre fetch ( ` $ { substanTl } / index` , { |
| Méthode : «Post» , |
| corps : commentaire , |
| En-têtes : { |
| "contenu-type" : "application / json" |
| } |
| } ) . alors ( ( ) => { getAllComments ( ) ; } ) ; |
| } |
Hello World Commentaire

Authentification et autorisation (identité)
J'ai utilisé l'identité NUGET et un contexte séparé afin d'authentifier et d'autoriser les utilisateurs dans mon application Web et de s'inscrire et de plier dans les Handels par des aides modèles nommés LoginModel et SignUpModel dans l'application Il existe 3 types d'utilisateurs "Admin", "User" ADN Anonymous. Le rôle du gestionnaire peut utiliser le contrôleur Manager et a ancre NAV-link pour la création et la mise à jour. Chaque utilisateur signé peut commenter les animaux dans l'application (y compris les gestionnaires). L'utilisateur anonyme ne peut que faire défiler la page du catalogue Animels ou enregistrer / se connecter.
Action d'enregistrement:
| [ Httppost ] |
| [ ValidateantiforgeryToken ] |
| Tâche asynchrone publique <IActionResult> Registre ( utilisateur SignUpModel ) |
| { |
| if ( ModelState . Isvalid ) |
| { |
| IDIDITYUSER IDUSER = NOUVEAU IdentityUser |
| { |
| Nom d'utilisateur = utilisateur . Nom d'utilisateur , |
| Phonenumber = utilisateur . Phonenumber , |
| E-mail = utilisateur . E-mail |
| } ; |
| VAR CREAREESULT = AWAIT _USERMANAGER . CreateSync ( idUser , utilisateur . Mot de passe ) ; |
| var addRole = attendre _UserManager . AddToroleAsync ( idUser , "utilisateur" ) ; |
| if ( createresult . a réussi ) |
| { |
| var SignUpResult = Await _signInManager . PasswordSignInasync ( utilisateur . Nom d'utilisateur , utilisateur . Mot de passe , faux , faux ) ; |
| if ( SignupResult . Succédé ) |
| { |
| return redirectToaction ( "index" , "home" ) ; |
| } |
| return Login ( ) ; |
| } |
| } |
| return View ( ) ; |
| } |
Tests unitaires
Cette solution d'application Web comprend un projet Xunit de test pour la vérification et la validation de la classe ReposiroEyBase pour les méthodes Sync et Async.
Exemple de test:
| [ Test , BesoinShread ] |
| public void findbyidasync ( ) |
| { |
| _CategoryRepository ? . Créer ( catégorie-test ! ) ; |
| _animelRepository ? . Créer ( AnimElTest ! ) ; |
| _CommentRepository ? . Créer ( commenttest ! ) ; |
| |
| Tâche <Animal> AnimElfound = _animelRepository ! . FindByIdasync ( AnimElTest ! . ID ) ; |
| AnimElfound . Continu with ( _ => { assert . That ( ani -Lelfound . Result , is . Equalto ( animeltest ) ) ; } ) ; |
| Tâche <Animal> animelnotfound = _animelRepository . FindByIdasync ( do_not_insret_animel ! . id ) ; |
| animelnotfound . Continu with ( _ => { assert . That ( ani -Lelfound . Result , est . Null ) ; } ) ; |
| Tâche <commentaire> commentfound = _commentRepository ! . Findbyidasync ( commenttest ! . commentID ) ; |
| commentaire . Continu With ( _ => { Assert . That ( commentfound . Result , est . EqualTo ( commenttest ) ) ; } ) ; |
| Tâche < catégories > catégoriefound = _categoryRepository ! . FindByIdasync ( catégorie-test ! . catégorieID ) ; |
| catégorie Found . Continu with ( _ => { assert . That ( categoryfound . Result , is . Equalto ( categorytest ) ) ; } ) ; |
| } |