Bygg en MVC-webapplikasjon med Grails

1. Oversikt

I denne veiledningen lærer vi hvordan du lager en enkel webapplikasjon ved hjelp av Grails.

Grails (nærmere bestemt den siste store versjonen) er et rammeverk som er bygget på toppen av Spring Boot-prosjektet og bruker Apache Groovy-språket til å utvikle webapper.

Den er inspirert av Rails Framework for Ruby og er bygget rundt konvensjon-over-konfigurasjonsfilosofien som gjør det mulig å redusere kjeleplatekoden.

2. Oppsett

Først av alt, la oss gå over til den offisielle siden for å forberede miljøet. På tidspunktet for denne veiledningen er den siste versjonen 3.3.3.

Enkelt sagt, det er to måter å installere Grails på: via SDKMAN eller ved å laste ned distribusjonen og legge til binære filer i PATH-miljøvariabelen.

Vi dekker ikke oppsettet trinnvis fordi det er godt dokumentert i Grails Docs.

3. Anatomy of a Grails App

I denne delen vil vi få en bedre forståelse av Grails applikasjonsstruktur. Som vi nevnte tidligere, foretrekker Grails konvensjon fremfor konfigurasjon, og derfor definerer plasseringen av filer deres formål. La oss se hva vi har i grails-app katalog:

  • eiendeler - et sted hvor vi lagrer statiske filer som stiler, javascript-filer eller bilder
  • konf - inneholder prosjektkonfigurasjonsfiler:
    • application.yml inneholder standard nettapp-innstillinger som datakilde, mime-typer og andre Grails eller Spring-relaterte innstillinger
    • resources.groovy inneholder vårbønnedefinisjoner
    • logback.groovy inneholder loggkonfigurasjon
  • kontrollere - ansvarlig for å håndtere forespørsler og generere svar eller delegere dem til synspunktene. Etter konvensjon, når et filnavn slutter med * Kontroller, oppretter rammeverket en standard URL-kartlegging for hver handling definert i kontrollerklassen
  • domene - inneholder forretningsmodellen for Grails-applikasjonen. Hver klasse som bor her vil bli kartlagt til databasetabeller av GORM
  • i18n - brukt til støtte for internasjonalisering
  • i det - et inngangspunkt for søknaden
  • tjenester - forretningslogikken til søknaden vil leve her. Etter konvensjon vil Grails lage en Spring singleton bønne for hver tjeneste
  • taglib - stedet for tilpassede tagbiblioteker
  • utsikt - inneholder visninger og maler

4. En enkel webapplikasjon

I dette kapittelet vil vi lage en enkel webapp for administrering av studenter. La oss starte med å påkalle CLI-kommandoen for å lage et applikasjonsskjelett:

grails create-app

Når den grunnleggende strukturen til prosjektet er generert, la oss gå videre til implementering av faktiske webappkomponenter.

4.1. Domenelag

Når vi implementerer en webapplikasjon for håndtering av studenter, la oss begynne med å generere en domeneklasse kalt Student:

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

Og til slutt, la oss legge til fornavn og etternavn egenskaper til det:

class Student {Streng fornavn Streng etternavn}

Grails bruker sine konvensjoner og vil sette opp en objekt-relasjonell kartlegging for alle klasser i grails-app / domene katalog.

Dessuten takket være GormEntity-egenskapen, alle domeneklasser har tilgang til alle CRUD-operasjoner, som vi bruker i neste avsnitt for implementering av tjenester.

4.2. Servicelag

Søknaden vår vil håndtere følgende brukssaker:

  • Viser en liste over studenter
  • Skape nye studenter
  • Fjerne eksisterende studenter

La oss implementere disse brukssakene. Vi starter med å generere en serviceklasse:

grails create-service com.baeldung.grails.Student

La oss gå over til grails-app / tjenester katalog, finn vår nyopprettede tjeneste i riktig pakke og legg til alle nødvendige metoder:

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

Merk at tjenester ikke støtter transaksjoner som standard. Vi kan aktivere denne funksjonen ved å legge til @Transaksjonell kommentar til klassen.

4.3. Kontrollerlag

For å gjøre forretningslogikken tilgjengelig for brukergrensesnittet, la oss lage en StudentController ved å påkalle følgende kommando:

grails create-controller com.baeldung.grails.Student

Som standard, Grails injiserer bønner ved navn. Det betyr at vi enkelt kan injisere StudentService singleton-forekomst i kontrolleren vår ved å erklære en forekomstvariabel kalt studentservice.

Vi kan nå definere handlinger for å lese, opprette og slette studenter.

klasse 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"}}

Etter konvensjon, de indeks () handling fra denne kontrolleren blir kartlagt til URI / student / indeks, de vise fram() handling til / student / show og så videre.

4.4. Vis lag

Etter å ha satt opp kontrollerhandlingene våre, kan vi nå fortsette å lage UI-visningene. Vi vil lage tre Groovy Server-sider for oppføring, opprettelse og fjerning av studenter.

Etter konvensjon vil Grails gjengi et syn basert på kontrollerens navn og handling. For eksempel,indeksen () handling fra StudentController vil løse seg til /grails-app/views/student/index.gsp

La oss starte med å implementere visningen / grails-app /visninger / student / indeks.gsp, som viser en liste over studenter. Vi bruker merkelappen for å lage en HTML-tabell som viser alle elevene som er returnert fra indeks () handling i kontrolleren vår.

Etter konvensjon, når vi svarer med en liste over gjenstander, Grails vil legge til "List" -suffikset i modellnavnet slik at vi får tilgang til listen over studentobjekter med variabelen studentliste:

  • Skape

Vi fortsetter nå til visningen / grails-app /utsikt /student / create.gsp, som lar brukeren opprette nye studenter. Vi bruker den innebygde tag, som viser et skjema for alle egenskapene til en gitt bønne:

Til slutt, la oss lage visningen / grails-app /utsikt /student / show.gsp for visning og til slutt sletting av studenter.

Blant andre koder vil vi dra nytte av , som tar en bønne som argument og viser alle feltene:

  • Studenterliste

4.5. Enhetstester

Grails utnytter hovedsakelig Spock til testformål. Hvis du ikke er kjent med Spock, anbefaler vi sterkt å lese denne opplæringen først.

La oss starte med enhetstesting av indeks () handling av vår StudentController.

Vi håner liste() metode fra StudentService og test om indeks () returnerer forventet modell:

ugyldig "Test indekshandlingen returnerer riktig modell" () {gitt: controller.studentService = Mock (StudentService) {liste () >> [ny Student (fornavn: 'John', etternavn: 'Doe')]} når: "Indekshandlingen utføres" controller.index () deretter: "Modellen er riktig" model.studentList.size () == 1 model.studentList [0] .firName == 'John' model.studentList [0]. etternavn == 'Doe'}

La oss nå teste slett () handling. Vi bekrefter om slett () ble påkalt fra StudentService og bekreft omdirigering til indeksiden:

ugyldig "Test slettehandlingen med en forekomst" () {gitt: controller.studentService = Mock (StudentService) {1 * slett (2)} når: "Domeneforekomsten sendes til slettingshandlingen" request.contentType = FORM_CONTENT_TYPE forespørsel .method = 'SLETT' controller.delete (2) deretter: "Brukeren blir omdirigert til indeks" response.redirectedUrl == '/ student / index'}

4.6. Integrasjonstester

Deretter, la oss ta en titt på hvordan du lager integrasjonstester for tjenestelaget. Hovedsakelig vil vi teste integrering med en database konfigurert i grails-app / conf / application.yml.

Som standard bruker Grails H2-databasen i minnet for dette formålet.

Først av alt, la oss begynne med å definere en hjelpemetode for å lage data for å fylle databasen:

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}

Takk til @Rollback kommentar på vår integrasjonstestklasse, hver metode vil kjøre i en egen transaksjon, som vil bli rullet tilbake på slutten av testen.

Ta en titt på hvordan vi implementerte integrasjonstesten for vår liste() metode:

ugyldig "testliste" () {setupData () når: Liste studentList = studentService.list () deretter: studentList.size () == 3 studentList [0] .lastName == 'Doe' studentList [1] .lastName == 'Foo' studentList [2] .lastName == 'Bar'}

La oss også teste slett () metode og validere hvis det totale antallet studenter blir redusert med en:

ugyldig "test delete" () {Long id = setupData () expect: studentService.list (). size () == 3 when: studentService.delete (id) sessionFactory.currentSession.flush () then: studentService.list () .størrelse () == 2}

5. Løping og distribusjon

Å kjøre og distribuere apper kan gjøres ved å påkalle én kommando via Grails CLI.

For å kjøre appen, bruk:

grails run-app

Som standard vil Grails sette opp Tomcat på port 8080.

La oss navigere til // localhost: 8080 / student / indeks for å se hvordan webapplikasjonen vår ser ut:

Hvis du vil distribuere applikasjonen til en servletbeholder, bruk:

gralskrig

for å lage en krigsgjenstand som er klar til distribusjon.

6. Konklusjon

I denne artikkelen fokuserte vi på hvordan du oppretter en Grails-webapplikasjon ved hjelp av konvensjon-over-konfigurasjonsfilosofien. Vi så også hvordan vi skal utføre enhets- og integrasjonstester med Spock-rammeverket.

Som alltid kan all koden som brukes her, bli funnet på GitHub.