Bangun Aplikasi Web MVC dengan Grails

1. Ikhtisar

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

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

Ini terinspirasi oleh Rails Framework for Ruby dan dibangun dengan filosofi convention-over-configuration yang memungkinkan pengurangan kode boilerplate .

2. Penyiapan

Pertama-tama, mari menuju ke halaman resmi untuk mempersiapkan lingkungan. Pada saat tutorial ini, versi terbaru adalah 3.3.3.

Sederhananya, ada dua cara untuk menginstal Grails: melalui SDKMAN atau dengan mengunduh distribusi dan menambahkan binari ke variabel lingkungan PATH.

Kami tidak akan membahas penyiapan langkah demi langkah karena ini didokumentasikan dengan baik di Dokumen Grails.

3. Anatomi Aplikasi Grails

Pada bagian ini, kita akan mendapatkan pemahaman yang lebih baik tentang struktur aplikasi Grails. Seperti yang kami sebutkan sebelumnya, Grails lebih memilih konvensi daripada konfigurasi, oleh karena itu lokasi file menentukan tujuannya. Mari kita lihat apa yang kita miliki di direktori grails-app :

  • assets - tempat kami menyimpan file aset statis seperti gaya, file javascript, atau gambar
  • conf - berisi file konfigurasi proyek:
    • application.yml berisi pengaturan aplikasi web standar seperti sumber data, jenis mime, dan pengaturan terkait Grails atau Spring lainnya
    • resources.groovy berisi definisi kacang musim semi
    • logback.groovy berisi konfigurasi logging
  • controller - bertanggung jawab untuk menangani permintaan dan menghasilkan respon atau mendelegasikannya ke view. Secara konvensi, ketika nama file diakhiri dengan * Controller , framework membuat pemetaan URL default untuk setiap tindakan yang ditentukan di kelas controller
  • domain - berisi model bisnis aplikasi Grails. Setiap kelas yang tinggal di sini akan dipetakan ke tabel database oleh GORM
  • i18n - digunakan untuk dukungan internasionalisasi
  • init - titik masuk aplikasi
  • layanan - logika bisnis aplikasi akan tinggal di sini. Secara konvensi, Grails akan membuat kacang tunggal Spring untuk setiap layanan
  • taglib - tempat untuk pustaka tag kustom
  • views - berisi tampilan dan template

4. Aplikasi Web Sederhana

Pada bab ini, kami akan membuat aplikasi web sederhana untuk mengelola Siswa. Mari kita mulai dengan menjalankan perintah CLI untuk membuat kerangka aplikasi:

grails create-app

Ketika struktur dasar proyek telah dibuat, mari kita lanjutkan ke penerapan komponen aplikasi web yang sebenarnya.

4.1. Lapisan Domain

Saat kita mengimplementasikan aplikasi web untuk menangani Siswa, mari kita mulai dengan membuat kelas domain yang disebut Siswa :

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

Dan terakhir, mari tambahkan properti firstName dan lastName ke dalamnya:

class Student { String firstName String lastName }

Grails menerapkan konvensi dan akan menyiapkan pemetaan relasional objek untuk semua kelas yang terletak di direktori grails-app / domain .

Selain itu, berkat sifat GormEntity, semua kelas domain akan memiliki akses ke semua operasi CRUD , yang akan kita gunakan di bagian selanjutnya untuk mengimplementasikan layanan.

4.2. Lapisan Layanan

Aplikasi kami akan menangani kasus penggunaan berikut:

  • Melihat daftar siswa
  • Menciptakan siswa baru
  • Menghapus siswa yang sudah ada

Mari terapkan kasus penggunaan ini. Kami akan mulai dengan membuat kelas layanan:

grails create-service com.baeldung.grails.Student

Mari menuju ke direktori grails-app / services , temukan layanan yang baru kita buat di paket yang sesuai dan tambahkan semua metode 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 bahwa layanan tidak mendukung transaksi secara default . Kita dapat mengaktifkan fitur ini dengan menambahkan anotasi @Transactional ke kelas.

4.3. Lapisan Pengontrol

Untuk membuat logika bisnis tersedia untuk UI, mari buat StudentController dengan menjalankan perintah berikut:

grails create-controller com.baeldung.grails.Student

Secara default, Grails menyuntikkan kacang dengan nama . Ini berarti bahwa kita dapat dengan mudah memasukkan instance tunggal StudentService ke dalam pengontrol dengan mendeklarasikan variabel instance yang disebut studentsService .

Kami sekarang dapat menentukan tindakan untuk membaca, membuat, dan menghapus siswa.

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

Berdasarkan konvensi, aksi index () dari pengontrol ini akan dipetakan ke URI / student / index , aksi show () ke / student / show dan seterusnya.

4.4. Lihat Layer

Setelah menyiapkan tindakan pengontrol kami, sekarang kami dapat melanjutkan untuk membuat tampilan UI. Kami akan membuat tiga Halaman Server Groovy untuk mendaftar, membuat dan menghapus Siswa.

Secara konvensi, Grails akan membuat tampilan berdasarkan nama pengontrol dan tindakan. Misalnya, aksi index () dari StudentController akan menyelesaikan ke /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

Pada artikel ini, kami fokus pada cara membuat aplikasi web Grails menggunakan filosofi konvensi-over-konfigurasi. Kami juga melihat cara melakukan pengujian unit dan integrasi dengan kerangka kerja Spock.

Seperti biasa, semua kode yang digunakan di sini dapat ditemukan di GitHub.