Construire une application Web MVC avec Grails

Construire une application Web MVC avec Grails

 

1. Vue d'ensemble

Dans ce didacticiel, nous allons apprendre à créer une application Web simple à l'aide deGrails.

Grails (plus précisément la dernière version majeure) est un framework construit sur le projet Spring Boot et utilise le langage Apache Groovy pour développer des applications Web.

Il est inspiré du framework Rails pour Ruby etis built around the convention-over-configuration philosophy which allows reducing boilerplate code.

2. Installer

Tout d’abord, allons sur la page officielle pour préparer l’environnement. Au moment de ce tutoriel, la dernière version est la 3.3.3.

En termes simples, il existe deux façons d’installer Grails: via SDKMAN ou en téléchargeant la distribution et en ajoutant des fichiers binaires à la variable d’environnement PATH.

Nous ne couvrirons pas la configuration étape par étape car elle est bien documentée dans lesGrails Docs.

3. Anatomie d'une application Grails

Dans cette section, nous comprendrons mieux la structure d’application Grails. Comme nous l'avons mentionné précédemment, Grails préfère la convention à la configuration. Par conséquent, l'emplacement des fichiers définit leur objectif. Voyons ce que nous avons dans le répertoiregrails-app:

  • assets - un endroit où nous stockons des fichiers d'actifs statiques tels que des styles, des fichiers javascript ou des images

  • conf - contient les fichiers de configuration du projet:

    • application.yml contient des paramètres d'application Web standard tels que la source de données, les types mime et d'autres paramètres liés à Grails ou Spring

    • resources.groovy contient des définitions de haricot printanier

    • logback.groovy contient la configuration de la journalisation

  • controllers - responsable de la gestion des demandes et de la génération des réponses ou de leur délégation aux vues. Par convention, lorsqu'un nom de fichier se termine par*Controller, le framework crée un mappage d'URL par défaut pour chaque action définie dans la classe de contrôleur

  • domain - contient le modèle commercial de l'application Grails. Chaque classe résidant ici sera mappée aux tables de la base de données par GORM

  • i18n - utilisé pour la prise en charge de l'internationalisation

  • init - un point d'entrée de l'application

  • services - la logique métier de l'application vivra ici. Par convention, Grails créera un haricot Spring Singleton pour chaque service.

  • taglib - l'emplacement des bibliothèques de balises personnalisées

  • views - contient des vues et des modèles

4. Une application Web simple

Dans ce chapitre, nous allons créer une application Web simple pour gérer les étudiants. Commençons par appeler la commande CLI pour créer un squelette d'application:

grails create-app

Une fois la structure de base du projet générée, passons à la mise en œuvre des composants réels de l'application Web.

4.1. Couche de domaine

Alors que nous implémentons une application Web pour gérer les étudiants, commençons par générer une classe de domaine appeléeStudent:

grails create-domain-class com.example.grails.Student

Et enfin, ajoutons-y les propriétésfirstName etlastName:

class Student {
    String firstName
    String lastName
}

Grails applique ses conventions et mettra en place un mappage objet-relationnel pour toutes les classes situées dans le répertoiregrails-app/domain.

De plus, grâce au traitGormEntity,all domain classes will have access to all CRUD operations, que nous utiliserons dans la section suivante pour implémenter des services.

4.2. Couche de service

Notre application traitera les cas d'utilisation suivants:

  • Affichage d'une liste d'étudiants

  • Créer de nouveaux étudiants

  • Retrait des étudiants existants

Implémentons ces cas d'utilisation. Nous allons commencer par générer une classe de service:

grails create-service com.example.grails.Student

Rendons-nous dans le répertoiregrails-app/services, trouvons notre nouveau service dans le package approprié et ajoutons toutes les méthodes nécessaires:

@Transactional
class StudentService {

    def get(id){
        Student.get(id)
    }

    def list() {
        Student.list()
    }

    def save(student){
        student.save()
    }

    def delete(id){
        Student.get(id).delete()
    }
}

Note that services don’t support transactions by default. Nous pouvons activer cette fonctionnalité en ajoutant l'annotation@Transactional à la classe.

4.3. Couche de contrôleur

Afin de rendre la logique métier disponible pour l'interface utilisateur, créons unStudentController en appelant la commande suivante:

grails create-controller com.example.grails.Student

Par défaut,Grails injects beans by names. Cela signifie que nous pouvons facilement injecter l'instance du singletonStudentService dans notre contrôleur en déclarant une variable d'instance appeléestudentsService.

Nous pouvons maintenant définir des actions pour lire, créer et supprimer des étudiants.

class StudentController {

    def studentService

    def index() {
        respond studentService.list()
    }

    def show(Long id) {
        respond studentService.get(id)
    }

    def create() {
        respond new Student(params)
    }

    def save(Student student) {
        studentService.save(student)
        redirect action:"index", method:"GET"
    }

    def delete(Long id) {
        studentService.delete(id)
        redirect action:"index", method:"GET"
    }
}

Par convention,the index() action from this controller will be mapped to the URI*/student/index*, l'actionshow() vers/student/show et ainsi de suite.

4.4. Afficher le calque

Après avoir configuré nos actions de contrôleur, nous pouvons maintenant procéder à la création des vues de l'interface utilisateur. Nous allons créer trois pages Groovy Server pour répertorier, créer et supprimer des étudiants.

Par convention, Grails affichera une vue en fonction du nom du contrôleur et de l'action. Par exemple,the index()action from StudentController will resolve to /grails-app/views/student/index.gsp

Commençons par implémenter la vue/grails-app/views/student/index.gsp, qui affichera une liste d'étudiants. Nous utiliserons la balise<f:table/> pour créer un tableau HTML affichant tous les élèves renvoyés par l'actionindex() dans notre contrôleur.

Par convention, lorsque nous répondons avec une liste d'objets,Grails will add the “List” suffix to the model name pour pouvoir accéder à la liste des objets étudiants avec la variablestudentList:



    
        
    
    
        
        

Nous allons maintenant passer à la vue/grails-app/views/student/create.gsp, qui permet à l'utilisateur de créer de nouveaux étudiants. Nous utiliserons la balise intégrée<f:all/>, qui affiche un formulaire pour toutes les propriétés d'un bean donné:



    
        
    
    
        

Enfin, créons la vue/grails-app/views/student/show.gsp pour afficher et éventuellement supprimer les élèves.

Parmi les autres balises, nous allons profiter de<f:display/>, qui prend un bean comme argument et affiche tous ses champs:



    
        
    
    
        
        

4.5. Tests unitaires

Grails utilise principalementSpock à des fins de test. Si vous n'êtes pas familier avec Spock, nous vous recommandons vivement de lire d'abordthis tutorial.

Commençons par les tests unitaires de l'actionindex() de nosStudentController.

Nous allons simuler la méthodelist() deStudentService et tester siindex() renvoie le modèle attendu:

void "Test the index action returns the correct model"() {
    given:
    controller.studentService = Mock(StudentService) {
        list() >> [new Student(firstName: 'John',lastName: 'Doe')]
    }

    when:"The index action is executed"
    controller.index()

    then:"The model is correct"
    model.studentList.size() == 1
    model.studentList[0].firstName == 'John'
    model.studentList[0].lastName == 'Doe'
}

Maintenant, testons l’actiondelete(). Nous allons vérifier sidelete() a été appelé depuisStudentService et vérifier la redirection vers la page d'index:

void "Test the delete action with an instance"() {
    given:
    controller.studentService = Mock(StudentService) {
      1 * delete(2)
    }

    when:"The domain instance is passed to the delete action"
    request.contentType = FORM_CONTENT_TYPE
    request.method = 'DELETE'
    controller.delete(2)

    then:"The user is redirected to index"
    response.redirectedUrl == '/student/index'
}

4.6. Tests d'intégration

Voyons ensuite comment créer des tests d'intégration pour la couche de service. Nous testerons principalement l'intégration avec une base de données configurée engrails-app/conf/application.yml.

By default, Grails uses the in-memory H2 database à cet effet.

Tout d’abord, commençons par définir une méthode d’assistance pour créer des données afin de remplir la base de données:

private Long setupData() {
    new Student(firstName: 'John',lastName: 'Doe')
      .save(flush: true, failOnError: true)
    new Student(firstName: 'Max',lastName: 'Foo')
      .save(flush: true, failOnError: true)
    Student student = new Student(firstName: 'Alex',lastName: 'Bar')
      .save(flush: true, failOnError: true)
    student.id
}

Grâce à l'annotation@Rollback sur notre classe de test d'intégration,each method will run in a separate transaction, which will be rolled back at the end of the test.

Regardez comment nous avons implémenté le test d'intégration pour notre méthodelist():

void "test list"() {
    setupData()

    when:
    List studentList = studentService.list()

    then:
    studentList.size() == 3
    studentList[0].lastName == 'Doe'
    studentList[1].lastName == 'Foo'
    studentList[2].lastName == 'Bar'
}

Testons également la méthodedelete() et vérifions si le nombre total d’élèves est décrémenté de un:

void "test delete"() {
    Long id = setupData()

    expect:
    studentService.list().size() == 3

    when:
    studentService.delete(id)
    sessionFactory.currentSession.flush()

    then:
    studentService.list().size() == 2
}

5. Exécution et déploiement

L'exécution et le déploiement d'applications peuvent être effectués en appelant une seule commande via Grails CLI.

Pour utiliser l'application, utilisez:

grails run-app

Par défaut, Grails installera Tomcat sur le port 8080.

Accédez àhttp://localhost:8080/student/index pour voir à quoi ressemble notre application Web:

image

Si vous souhaitez déployer votre application dans un conteneur de servlets, utilisez:

grails war

créer un artefact de guerre prêt à être déployé.

6. Conclusion

Dans cet article, nous nous sommes concentrés sur la création d'une application Web Grails à l'aide de la philosophie convention sur configuration. Nous avons également vu comment effectuer des tests unitaires et d’intégration avec le framework Spock.

Comme toujours, tout le code utilisé ici peut être trouvéover on GitHub.