Создайте веб-приложение MVC с Grails

Создайте веб-приложение MVC с Grails

 

1. обзор

В этом руководстве мы узнаем, как создать простое веб-приложение с помощьюGrails.

Grails (точнее, последняя основная версия) - это фреймворк, построенный на основе проекта Spring Boot и использующий язык Apache Groovy для разработки веб-приложений.

Он вдохновлен Rails Framework для Ruby иis built around the convention-over-configuration philosophy which allows reducing boilerplate code.

2. Настроить

Прежде всего, давайте перейдем на официальную страницу, чтобы подготовить среду. На момент написания данного руководства последняя версия 3.3.3.

Проще говоря, существует два способа установки Grails: через SDKMAN или путем загрузки дистрибутива и добавления двоичных файлов в переменную среды PATH.

Мы не будем описывать настройку шаг за шагом, потому что она хорошо задокументирована вGrails Docs.

3. Анатомия приложения Grails

В этом разделе мы получим лучшее понимание структуры приложения Grails. Как мы упоминали ранее, Grails предпочитает соглашение по конфигурации, поэтому расположение файлов определяет их назначение. Посмотрим, что у нас есть в каталогеgrails-app:

  • assets - место, где мы храним файлы статических ресурсов, такие как стили, файлы javascript или изображения.

  • conf - содержит файлы конфигурации проекта:

    • application.yml содержит стандартные настройки веб-приложения, такие как источник данных, типы mime и другие настройки, связанные с Grails или Spring.

    • resources.groovy содержит определения компонентов Spring

    • logback.groovy содержит конфигурацию журналирования

  • controllers - отвечает за обработку запросов и генерацию ответов или делегирование их представлениям. По соглашению, когда имя файла заканчивается на*Controller, платформа создает сопоставление URL-адресов по умолчанию для каждого действия, определенного в классе контроллера.

  • domain - содержит бизнес-модель приложения Grails. Каждый класс, живущий здесь, будет отображаться в таблицах базы данных GORM

  • i18n - используется для поддержки интернационализации

  • init - точка входа в приложение

  • services - здесь будет жить бизнес-логика приложения. По соглашению Grails создаст одноэлементный компонент Spring для каждого сервиса

  • taglib - место для пользовательских библиотек тегов

  • views - содержит представления и шаблоны

4. Простое веб-приложение

В этой главе мы создадим простое веб-приложение для управления студентами. Начнем с вызова команды CLI для создания каркаса приложения:

grails create-app

Когда основная структура проекта сформирована, перейдем к реализации реальных компонентов веб-приложения.

4.1. Уровень домена

Поскольку мы реализуем веб-приложение для работы со студентами, давайте начнем с создания класса домена под названиемStudent:

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

И наконец, добавим к нему свойстваfirstName иlastName:

class Student {
    String firstName
    String lastName
}

Grails применяет свои соглашения и устанавливает объектно-реляционное отображение для всех классов, расположенных в каталогеgrails-app/domain.

Более того, благодаря трейтуGormEntityall domain classes will have access to all CRUD operations, который мы будем использовать в следующем разделе для реализации сервисов.

4.2. Сервисный уровень

Наше приложение будет обрабатывать следующие варианты использования:

  • Просмотр списка студентов

  • Создание новых студентов

  • Удаление существующих студентов

Давайте реализуем эти варианты использования. Начнем с создания класса обслуживания:

grails create-service com.example.grails.Student

Перейдем в каталогgrails-app/services, найдем нашу недавно созданную службу в соответствующем пакете и добавим все необходимые методы:

@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. Мы можем включить эту функцию, добавив к классу аннотацию@Transactional.

4.3. Уровень контроллера

Чтобы сделать бизнес-логику доступной для пользовательского интерфейса, давайте создадимStudentController, вызвав следующую команду:

grails create-controller com.example.grails.Student

По умолчаниюGrails injects beans by names. Это означает, что мы можем легко внедрить синглтон-экземплярStudentService в наш контроллер, объявив переменную экземпляра с именемstudentsService.

Теперь мы можем определить действия для чтения, создания и удаления студентов.

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

По соглашениюthe index() action from this controller will be mapped to the URI*/student/index*, действиеshow() на/student/show и так далее.

4.4. Просмотр слоя

Настроив действия контроллера, мы можем приступить к созданию представлений пользовательского интерфейса. Мы создадим три страницы сервера Groovy для перечисления, создания и удаления студентов.

По соглашению Grails будет отображать представление на основе имени контроллера и действия. Например,the index()action from StudentController will resolve to /grails-app/views/student/index.gsp

Начнем с реализации представления/grails-app/views/student/index.gsp, которое будет отображать список студентов. Мы будем использовать тег<f:table/> для создания таблицы HTML, отображающей всех учащихся, возвращенных из действияindex() в нашем контроллере.

По соглашению, когда мы отвечаем списком объектовGrails will add the “List” suffix to the model name, чтобы мы могли получить доступ к списку объектов студентов с переменнойstudentList:



    
        
    
    
        
        

Теперь перейдем к представлению/grails-app/views/student/create.gsp,, которое позволяет пользователю создавать новых студентов. Мы будем использовать встроенный тег<f:all/>, который отображает форму для всех свойств данного bean-компонента:



    
        
    
    
        

Наконец, давайте создадим представление/grails-app/views/student/show.gsp для просмотра и, в конечном итоге, удаления студентов.

Среди других тегов мы воспользуемся преимуществом<f:display/>, который принимает компонент в качестве аргумента и отображает все его поля:



    
        
    
    
        
        

4.5. Модульные тесты

Grails в основном используетSpock для тестирования. Если вы не знакомы со Споком, мы настоятельно рекомендуем сначала прочитатьthis tutorial.

Начнем с модульного тестирования действияindex() нашегоStudentController.

Мы имитируем методlist() изStudentService и проверим, возвращает лиindex() ожидаемую модель:

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

Теперь давайте проверим действиеdelete(). Мы проверим, был лиdelete() вызван изStudentService, и проверим перенаправление на страницу индекса:

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. Интеграционные тесты

Затем давайте посмотрим, как создавать интеграционные тесты для уровня сервиса. В основном мы будем тестировать интеграцию с базой данных, настроенной вgrails-app/conf/application.yml.

By default, Grails uses the in-memory H2 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
}

Благодаря аннотации@Rollback в нашем интеграционном тестовом классеeach method will run in a separate transaction, which will be rolled back at the end of the test.

Посмотрите, как мы реализовали интеграционный тест для нашего методаlist():

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

Также давайте протестируем методdelete() и проверим, уменьшается ли общее количество студентов на единицу:

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

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

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

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

5. Запуск и развертывание

Запуск и развертывание приложений можно выполнить, вызвав одну команду через интерфейс командной строки Grails.

Для запуска приложения используйте:

grails run-app

По умолчанию Grails настроит Tomcat на порт 8080.

Давайте перейдем кhttp://localhost:8080/student/index, чтобы увидеть, как выглядит наше веб-приложение:

image

Если вы хотите развернуть свое приложение в контейнере сервлета, используйте:

grails war

создать готовый к развертыванию военный артефакт.

6. Заключение

В этой статье мы сосредоточились на том, как создать веб-приложение Grails, используя философию соглашения о конфигурации. Мы также увидели, как выполнять модульные и интеграционные тесты с помощью инфраструктуры Spock.

Как всегда, весь используемый здесь код находится вover on GitHub.