Bouw een MVC-webapplicatie met Grails

1. Overzicht

In deze tutorial leren we hoe je een eenvoudige webapplicatie maakt met Grails.

Grails (meer bepaald de laatste grote versie) is een framework dat bovenop het Spring Boot-project is gebouwd en de Apache Groovy-taal gebruikt om webapps te ontwikkelen.

Het is geïnspireerd op het Rails Framework voor Ruby en is gebouwd rond de filosofie van conventie-over-configuratie die het mogelijk maakt om de standaardcode te verminderen.

2. Installatie

Laten we allereerst naar de officiële pagina gaan om de omgeving voor te bereiden. Op het moment van deze tutorial is de nieuwste versie 3.3.3.

Simpel gezegd, er zijn twee manieren om Grails te installeren: via SDKMAN of door de distributie te downloaden en binaire bestanden toe te voegen aan de PATH-omgevingsvariabele.

We zullen de installatie niet stap voor stap behandelen, omdat deze goed is gedocumenteerd in de Grails Docs.

3. Anatomie van een Grails-app

In deze sectie zullen we een beter begrip krijgen van de applicatiestructuur van Grails. Zoals we eerder vermeldden, geeft Grails de voorkeur aan conventie boven configuratie, daarom bepaalt de locatie van bestanden hun doel. Laten we eens kijken wat we hebben in de grails-app directory:

  • middelen - een plaats waar we statische activabestanden opslaan, zoals stijlen, javascript-bestanden of afbeeldingen
  • conf - bevat projectconfiguratiebestanden:
    • application.yml bevat standaard webapp-instellingen zoals gegevensbron, mime-typen en andere Grails of Spring-gerelateerde instellingen
    • resources.groovy bevat springboondefinities
    • logback.groovy bevat logboekconfiguratie
  • controllers - verantwoordelijk voor het afhandelen van verzoeken en het genereren van antwoorden of het delegeren hiervan aan de views. Volgens afspraak, wanneer een bestandsnaam eindigt op * Controller, creëert het framework een standaard URL-toewijzing voor elke actie die is gedefinieerd in de controller-klasse
  • domein - bevat het businessmodel van de Grails-applicatie. Elke klas die hier woont, wordt door GORM toegewezen aan databasetabellen
  • i18n - gebruikt voor ondersteuning van internationalisering
  • in het - een toegangspunt van de applicatie
  • Diensten - de bedrijfslogica van de applicatie zal hier leven. Volgens afspraak maakt Grails voor elke service een Spring-singletonboon
  • taglib - de plek voor aangepaste tagbibliotheken
  • keer bekeken - bevat weergaven en sjablonen

4. Een eenvoudige webapplicatie

In dit hoofdstuk maken we een eenvoudige webapp om studenten te beheren. Laten we beginnen met het aanroepen van de CLI-opdracht voor het maken van een applicatieskelet:

grails create-app

Als de basisstructuur van het project is gegenereerd, gaan we verder met het implementeren van daadwerkelijke webapp-componenten.

4.1. Domeinlaag

Nu we een webtoepassing implementeren voor het afhandelen van studenten, laten we beginnen met het genereren van een domeinklasse met de naam Leerling:

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

En tot slot, laten we de Voornaam en achternaam eigenschappen eraan:

class Student {String firstName String lastName}

Grails past zijn conventies toe en zal een object-relationele mapping opzetten voor alle klassen die zich in grails-app / domein directory.

Bovendien, dankzij de eigenschap GormEntity, alle domeinklassen hebben toegang tot alle CRUD-bewerkingen, die we in de volgende sectie zullen gebruiken voor het implementeren van services.

4.2. Servicelaag

Onze applicatie is geschikt voor de volgende gebruiksscenario's:

  • Een lijst met studenten bekijken
  • Nieuwe studenten aanmaken
  • Bestaande studenten verwijderen

Laten we deze use-cases implementeren. We beginnen met het genereren van een serviceklasse:

grails create-service com.baeldung.grails.Student

Laten we naar de grails-app / services directory, zoek onze nieuw gecreëerde service in het juiste pakket en voeg alle benodigde methoden toe:

@Transactional class StudentService {def get (id) {Student.get (id)} def lijst () {Student.list ()} def save (student) {student.save ()} def delete (id) {Student.get (id) .delete ()}}

Houd er rekening mee dat services standaard geen transacties ondersteunen. We kunnen deze functie inschakelen door het @Transactional annotatie aan de klas.

4.3. Controller-laag

Laten we een StudentController door het volgende commando aan te roepen:

grails create-controller com.baeldung.grails.Student

Standaard, Grails injecteert bonen met namen. Het betekent dat we de StudentService singleton-instantie in onze controller door een instantievariabele te declareren met de naam studentenService.

We kunnen nu acties definiëren voor het lezen, aanmaken en verwijderen van leerlingen.

class StudentController {def studentService def index () {reageer studentService.list ()} def show (lange id) {antwoord studentService.get (id)} def create () {reageer nieuwe student (params)} def save (student student) {studentService.save (student) omleidingsactie: "index", methode: "GET"} def delete (lange id) {studentService.delete (id) omleidingsactie: "index", methode: "GET"}}

Volgens afspraak, de inhoudsopgave() actie van deze controller wordt toegewezen aan de URI / student / index, de tonen() actie om / student / show enzovoorts.

4.4. Bekijk laag

Nadat we onze controlleracties hebben ingesteld, kunnen we doorgaan met het maken van de UI-weergaven. We zullen drie Groovy Server-pagina's maken voor het vermelden, maken en verwijderen van studenten.

Volgens afspraak geeft Grails een weergave op basis van de naam en actie van de controller. Bijvoorbeeld,de index() actie van StudentController zal oplossen /grails-app/views/student/index.gsp

Laten we beginnen met het implementeren van de weergave / grails-app /views / student / index.gsp, die een lijst met studenten zal weergeven. We gebruiken de tag om een ​​HTML-tabel te maken waarin alle leerlingen worden weergegeven die zijn teruggekeerd van het inhoudsopgave() actie in onze controller.

Volgens afspraak, wanneer we reageren met een lijst met objecten, Grails zal het achtervoegsel "List" aan de modelnaam toevoegen zodat we toegang hebben tot de lijst met leerlingobjecten met de variabele studentList:

  • Creëer

We gaan nu verder met de weergave / grails-app /keer bekeken/student / create.gsp, waarmee de gebruiker nieuwe studenten kan maken. We gebruiken de ingebouwde tag, die een formulier weergeeft voor alle eigenschappen van een bepaalde bean:

Laten we tot slot de weergave maken / grails-app /keer bekeken/student / show.gsp voor het bekijken en eventueel verwijderen van leerlingen.

We profiteren van onder andere tags , die een boon als argument neemt en al zijn velden weergeeft:

  • Studentenlijst

4.5. Eenheidstests

Grails maakt vooral gebruik van Spock voor testdoeleinden. Als u niet bekend bent met Spock, raden we u ten zeerste aan deze tutorial eerst te lezen.

Laten we beginnen met het testen van het inhoudsopgave() actie van onze StudentController.

We zullen de spot drijven met lijst() methode van StudentService en test of inhoudsopgave() geeft het verwachte model terug:

void "Test de indexactie geeft het juiste model terug" () {gegeven: controller.studentService = Mock (StudentService) {lijst () >> [nieuwe student (voornaam: 'Jan', achternaam: 'Doe')]} wanneer: "De indexactie wordt uitgevoerd" controller.index () en vervolgens: "Het model is correct" model.studentList.size () == 1 model.studentList [0] .firstName == 'John' model.studentList [0]. lastName == 'Doe'}

Laten we nu de verwijderen () actie. We zullen verifiëren of verwijderen () werd ingeroepen van StudentService en verifieer omleiding naar de indexpagina:

void "Test de verwijderactie met een instantie" () {gegeven: controller.studentService = Mock (StudentService) {1 * delete (2)} wanneer: "De domeinexemplaar wordt doorgegeven aan de verwijderactie" request.contentType = FORM_CONTENT_TYPE verzoek .method = 'DELETE' controller.delete (2) dan: "De gebruiker wordt omgeleid naar index" response.redirectedUrl == '/ student / index'}

4.6. Integratietests

Laten we vervolgens eens kijken hoe u integratietests voor de servicelaag kunt maken. We zullen voornamelijk de integratie testen met een database die is geconfigureerd in grails-app / conf / application.yml.

Grails gebruikt standaard de H2-database in het geheugen Voor dit doeleinde.

Laten we eerst beginnen met het definiëren van een hulpmethode voor het maken van gegevens om de database te vullen:

private Lange setupData () {nieuwe student (voornaam: 'Jan', achternaam: 'Doe') .save (flush: true, failOnError: true) nieuwe student (voornaam: 'Max', achternaam: 'Foo') .save ( flush: true, failOnError: true) Student student = nieuwe student (voornaam: 'Alex', achternaam: 'Bar') .save (flush: true, failOnError: true) student.id}

Dankzij de @Terugrollen annotatie op onze integratietestklasse, elke methode wordt in een afzonderlijke transactie uitgevoerd, die aan het einde van de test wordt teruggedraaid.

Bekijk hoe we de integratietest voor ons hebben geïmplementeerd lijst() methode:

void "test list" () {setupData () wanneer: List studentList = studentService.list () dan: studentList.size () == 3 studentList [0] .lastName == 'Doe' studentList [1] .lastName == 'Foo' studentList [2] .lastName == 'Bar'}

Laten we ook het verwijderen () methode en valideer of het totale aantal studenten met één wordt verlaagd:

void "test delete" () {Long id = setupData () verwacht: studentService.list (). size () == 3 wanneer: studentService.delete (id) sessionFactory.currentSession.flush () dan: studentService.list () .size () == 2}

5. Uitvoeren en implementeren

Het uitvoeren en implementeren van apps kan worden gedaan door een enkele opdracht aan te roepen via Grails CLI.

Gebruik voor het uitvoeren van de app:

grails run-app

Standaard stelt Grails Tomcat in op poort 8080.

Laten we naar // localhost: 8080 / student / index om te zien hoe onze webapplicatie eruit ziet:

Als u uw applicatie in een servlet-container wilt implementeren, gebruikt u:

grails oorlog

om een ​​gebruiksklaar oorlogsartefact te creëren.

6. Conclusie

In dit artikel hebben we ons gericht op het maken van een Grails-webtoepassing met behulp van de filosofie van conventie-over-configuratie. We hebben ook gezien hoe unit- en integratietests kunnen worden uitgevoerd met het Spock-framework.

Zoals altijd is alle code die hier wordt gebruikt, te vinden op GitHub.