Bina Aplikasi Web MVC dengan Grails

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan belajar bagaimana membuat aplikasi web sederhana menggunakan Grails.

Grails (lebih tepatnya versi utama terbaru) adalah kerangka kerja yang dibina di atas projek Spring Boot dan menggunakan bahasa Apache Groovy untuk mengembangkan aplikasi web.

Ini diilhamkan oleh Rails Framework for Ruby dan dibina berdasarkan falsafah konvensyen-over-konfigurasi yang memungkinkan mengurangkan kod plat boiler .

2. Persediaan

Pertama sekali, mari menuju ke laman rasmi untuk mempersiapkan persekitaran. Pada masa tutorial ini, versi terbaru adalah 3.3.3.

Ringkasnya, ada dua cara memasang Grails: melalui SDKMAN atau dengan memuat turun pengedaran dan menambahkan binari ke pemboleh ubah persekitaran PATH.

Kami tidak akan membahas penyediaan langkah demi langkah kerana didokumentasikan dengan baik di Grails Docs.

3. Anatomi Aplikasi Grails

Pada bahagian ini, kita akan mendapat pemahaman yang lebih baik mengenai struktur aplikasi Grails. Seperti yang telah kami sebutkan sebelumnya, Grails lebih memilih konvensi daripada konfigurasi, oleh itu lokasi fail menentukan tujuannya. Mari lihat apa yang kita ada di direktori aplikasi grails :

  • aset - tempat di mana kami menyimpan fail aset statik seperti gaya, fail javascript atau gambar
  • conf - mengandungi fail konfigurasi projek:
    • application.yml mengandungi tetapan aplikasi web standard seperti sumber data, jenis mime, dan tetapan lain yang berkaitan dengan Grail atau Spring
    • resources.groovy mengandungi definisi kacang musim bunga
    • logback.groovy mengandungi konfigurasi pembalakan
  • pengawal - bertanggungjawab untuk menangani permintaan dan menghasilkan respons atau mendelegasikannya kepada pandangan. Secara konvensional, apabila nama file diakhiri dengan * Controller , framework membuat pemetaan URL lalai untuk setiap tindakan yang ditentukan dalam kelas pengawal
  • domain - mengandungi model perniagaan aplikasi Grails. Setiap kelas yang tinggal di sini akan dipetakan ke jadual pangkalan data oleh GORM
  • i18n - digunakan untuk sokongan pengantarabangsaan
  • init - titik masuk aplikasi
  • perkhidmatan - logik perniagaan aplikasi akan tinggal di sini. Secara konvensyen, Grails akan membuat kacang tunggal Spring untuk setiap perkhidmatan
  • taglib - tempat untuk perpustakaan tag tersuai
  • paparan - mengandungi paparan dan templat

4. Aplikasi Web Mudah

Dalam bab ini, kami akan membuat aplikasi web mudah untuk menguruskan Pelajar. Mari mulakan dengan memanggil perintah CLI untuk membuat kerangka aplikasi:

grails create-app

Apabila struktur asas projek telah dihasilkan, mari kita beralih ke pelaksanaan komponen aplikasi web yang sebenarnya.

4.1. Lapisan Domain

Semasa kami melaksanakan aplikasi web untuk menangani Pelajar, mari kita mulakan dengan menghasilkan kelas domain yang disebut Pelajar :

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

Dan akhirnya, mari kita tambahkan sifat nama depan dan nama akhir :

class Student { String firstName String lastName }

Grails menerapkan konvensinya dan akan membuat pemetaan relasi-objek untuk semua kelas yang terdapat di direktori grails-app / domain .

Lebih-lebih lagi, terima kasih kepada sifat GormEntity, semua kelas domain akan mempunyai akses ke semua operasi CRUD , yang akan kami gunakan di bahagian seterusnya untuk melaksanakan perkhidmatan.

4.2. Lapisan Perkhidmatan

Aplikasi kami akan menangani kes penggunaan berikut:

  • Melihat senarai pelajar
  • Melahirkan pelajar baru
  • Mengeluarkan pelajar yang ada

Mari kita laksanakan kes penggunaan ini. Kami akan memulakan dengan menghasilkan kelas perkhidmatan:

grails create-service com.baeldung.grails.Student

Mari menuju ke direktori aplikasi-perkhidmatan / perkhidmatan grails , cari perkhidmatan yang baru dibuat dalam pakej yang sesuai dan tambahkan semua kaedah yang diperlukan:

@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() } }

Perhatikan bahawa perkhidmatan tidak menyokong transaksi secara lalai . Kami boleh mengaktifkan ciri ini dengan menambahkan anotasi @Transactional ke kelas.

4.3. Lapisan Pengawal

Untuk menjadikan logik perniagaan tersedia untuk UI, mari buat StudentController dengan menggunakan arahan berikut:

grails create-controller com.baeldung.grails.Student

Secara lalai, Grails menyuntik kacang dengan nama . Ini bermaksud bahawa kita dapat dengan mudah memasukkan contoh tunggal StudentService ke dalam pengawal kita dengan menyatakan pemboleh ubah instance yang disebut studentsService .

Kita sekarang dapat menentukan tindakan untuk membaca, membuat dan menghapus pelajar.

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

By convention, the index() action from this controller will be mapped to the URI /student/index, the show() action to /student/show and so on.

4.4. View Layer

Having set up our controller actions, we can now proceed to create the UI views. We will create three Groovy Server Pages for listing, creating and removing Students.

By convention, Grails will render a view based on controller name and action. For example,the index() action from StudentController will resolve to /grails-app/views/student/index.gsp

Let's start with implementing the view /grails-app/views/student/index.gsp, which will display a list of students. We'll use the tag to create an HTML table displaying all students returned from the index() action in our controller.

By convention, when we respond with a list of objects, Grails will add the “List” suffix to the model name so that we can access the list of student objects with the variable studentList:


    
  • Create

We'll now proceed to the view /grails-app/views/student/create.gsp, which allows the user to create new Students. We'll use the built-in tag, which displays a form for all properties of a given bean:

Finally, let's create the view /grails-app/views/student/show.gsp for viewing and eventually deleting students.

Among other tags, we'll take advantage of , which takes a bean as an argument and displays all its fields:


    
  • Students list

4.5. Unit Tests

Grails mainly takes advantage of Spock for testing purposes. If you are not familiar with Spock, we highly recommend reading this tutorial first.

Let's start with unit testing the index() action of our StudentController.

We'll mock the list() method from StudentService and test if index() returns the expected model:

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' }

Now, let's test the delete() action. We'll verify if delete() was invoked from StudentService and verify redirection to the index page:

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. Integration Tests

Next, let's have a look at how to create integration tests for the service layer. Mainly we'll test integration with a database configured in grails-app/conf/application.yml.

By default, Grails uses the in-memory H2 database for this purpose.

First of all, let's start with defining a helper method for creating data to populate the database:

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 }

Thanks to the @Rollback annotation on our integration test class, each method will run in a separate transaction, which will be rolled back at the end of the test.

Take a look at how we implemented the integration test for our list() method:

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' }

Also, let's test the delete() method and validate if the total count of students is decremented by one:

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

5. Running and Deploying

Running and deploying apps can be done by invoking single command via Grails CLI.

For running the app use:

grails run-app

By default, Grails will setup Tomcat on port 8080.

Let's navigate to //localhost:8080/student/index to see what our web application looks like:

If you want to deploy your application to a servlet container, use:

grails war

to create a ready-to-deploy war artifact.

6. Conclusion

Dalam artikel ini, kami memfokuskan cara membuat aplikasi web Grails menggunakan falsafah konvensyen-over-konfigurasi. Kami juga melihat bagaimana melakukan ujian unit dan integrasi dengan kerangka Spock.

Seperti biasa, semua kod yang digunakan di sini boleh didapati di GitHub.