Как использовать строительные леса и RESTfulness вместе в Grails 2.3

Официальная документация Grails гласит, что

Версия 2.0.x плагина для лесов включает в себя различные строительные леса шаблоны, совпадающие с новыми API REST, встроенными в Grails 2.3 и выше. (взято отсюда http://grails.org/doc/latest/guide/scaffolding.html)

Но я не могу сделать (или я не понимаю концепцию) работу RESTfulness вместе с лесами.

Начните с нуля:

grails create-app myapp
cd myapp/
grails create-domain-class Book
grails create-scaffold-controller myapp.Book

Добавить поле в класс домена

class Book {
    String text

    static constraints = {
    }
}

и запустите приложение с помощью grails run-app. Серфинг на http://localhost:8080/myapp/ показывает, что строительные леса отлично работают:

  • http://localhost:8080/myapp/book/index страница показывает список книг
  • http://localhost:8080/myapp/book/show/1 страница показывает подробности для книги с id = 1
  • http://localhost:8080/myapp/book/create страница создает книгу
  • и, следовательно, силы, старые добрые леса.

Посмотрим, что будет с REST. Официальные документы говорят, что я должен использовать URL-адреса, такие как http://localhost:8080/myapp/books/... для REST, но любые попытки доступа к приложению, вроде этого curl -i -H "Accept: application/json" localhost:8080/myapp/books/1 возвращает 404 с кучей HTML.

Хорошо, внимательно прочитайте документы:

Самый простой способ создать RESTful API в Grails - выставить класс домена как ресурс REST. Это можно сделать, добавив grails.rest.Resource преобразование в любой класс домена

Нет проблем, теперь заголовок класса книги

import grails.rest.*

@Resource(uri='/books') class Book {

Теперь серфинг на http://localhost:8080/myapp/ показывает, что строительные леса разбиты:

  • http://localhost:8080/myapp/book/index страница показывает список книг
  • http://localhost:8080/myapp/book/create отображается страница xml <?xml version="1.0" encoding="UTF-8"?><book><text /></book>
  • и так сильно, плохой новый xml-вывод.

Я играл с @Resource и "/books" (ресурсы: "книга" ) в URLMappings.groovy, но не нашел никакого рабочего решения, которое делает возможными строительные леса, а RESTfulness работает спина к спине. Действительно, мне удалось заставить их работать отдельно.

Обновление

Я нашел способ достижения желаемой цели. Я нашел:

  • Отметьте класс книги с помощью @Resource(uri = "/books").
  • Удалить контроллер леса. BookController.
  • Создайте выделенный контроллер с помощью лесов для книги: class HumanBookController {static scaffold = Book}

Теперь страницы графического интерфейса scaffold с URL-адресами, такими как http://localhost:8080/myapp/humanBook/index, работают очень хорошо. Любые запросы json обрабатываются с такими URL-адресами, как http://localhost:8080/myapp/books/1. Но не изящно иметь 2 контроллера, которые делают одинаковые вещи для обычных веб-сайтов и json.

Ответ 1

Вы можете сделать это:

import grails.rest.RestfulController

class BookController extends RestfulController {

    static responseFormats = ['html', 'json']

    BookController() {
        super(Book)
    }
}

И затем в UrlMappings.groovy:

 "/books"(resources:"book")
 "/$controller/$action?/$id?(.${format})?"{
    constraints {
        // apply constraints here
    }
  }

Не нужно добавлять @Resource в домене. Теперь у вас есть /books/ 1.json или/books/1.html, чтобы указать на нужные места. Вам может потребоваться сделать grails generate-view Book для создания представления. Но хотя вам нужно генерировать представления для html, вы сохраняете только один контроллер и путь.

Ответ 2

У меня были те же проблемы, что и у вас. Это может быть тривиальное решение, а не для каждого случая, но попробуйте обновить версию Grails. Что касается меня: Grails 2.3.4 → Grails 2.3.6 работал. Надеюсь, что кто-нибудь поможет.

Ответ 3

В настоящее время я использую Grails 2.4.0, решение получило это:

  • Контроллер: BookController {static scaffold = true}
  • Домен: Книга {....}//БЕЗ @Resource

В результате вы можете:

  • /book.json, чтобы получить список JSONized
  • /book/index, чтобы получить стандартные подклассы HTML
  • /book/create html scaffold для нового элемента
  • /book/show/1 html scaffold edit item 1
  • /book/show/1.json JSON для элемента id: 1

Я злой, я знаю. Я нашел этот, и это заставляет меня идти.

Ответ 4

С Grails 2.4.4 мне удалось получить леса, работающие с одним контроллером, используя следующие шаги:

  • Добавлен URL-адрес для сопоставления ресурсов в UrlMappings.groovy, например. "/books"(resources:"book")
  • Вставить static scaffold = true в сгенерированный контроллер

Я не проверял, изменилось ли следующее, но я также установил grails.mime.disable.accept.header.userAgents = [] и grails.mime.use.accept.header = true в Config.groovy(последнее, по-видимому, новое значение по умолчанию).

Оба подкласса REST и интерфейсы интерфейса работают нормально со следующими тестами:

  • GET/app//1 (передача заголовка Accept)
  • GET/app//1.json(нет заголовка Accept)
  • POST/app/(с полезной нагрузкой в ​​виде json или формы)
  • DELETE/app//1
  • PUT/app//1 (с полезной нагрузкой json полезной нагрузки обновил объект, но отправил обратно 302 перенаправления)

ИЗМЕНИТЬ

  • Убрал шаг аннотации ресурса и уточнил настройку сопоставления URL
  • URI, назначенный в сопоставлении URL, не совпадает с URI по умолчанию для контроллера. Например, "книги" вместо "книги". После добавления этого отображения URI для контроллера по умолчанию будет иметь URI в UrlMapping, но исходный URI будет продолжать работать.

Ответ 5

Сгенерированный контроллер является контроллером Restful, потому что он реализует действия, знакомые с запросами типа:

curl -i -X GET yourDomain:8080/yourApp/books.json

Он возвращает список книг в формате json. (10 книг, предполагая, что вы создали тестовые данные, не так ли?)

Вы можете добавить параметр, например:

curl -i -X GET yourDomain:8080/yourApp/books.xml?40

По умолчанию вы получите формат html. Вам нужно добавить .json или .xml, чтобы получить правильные данные.

Вы также можете использовать заголовок Accept

curl -i -X GET -H "Accept: application/xml" yourDomain/books/1

возвращает детали книги с id = 1 в формате xml. Наконец

curl -i -X POST -H "Content-Type: application/json" -d "{name: 'Book'}" yourDomain/books

создает новую книгу и

curl -i -X PUT -H "Content-Type: application/json" -d "{name: 'Book'}" yourDomain/books/1

обновляет имя книги с id = 1

Все ресурсы должны быть раскрыты через и URL. URL-адрес не создан для вас, вы должны записать его в файл UrlMappings:

"/v1/books"(resources: "book")

Если первая строка "/v1/books" - это uri, а вторая строка "book" - это имя контроллера, следующего за соглашением grails. (Предыдущая строка v1 - это то, что я всегда добавлял номер версии в мои URI API)

 | GET    | /v1/books            | Action: index  |
 | GET    | /v1/books/create     | Action: create |
 | POST   | /v1/books            | Action: save   |
 | GET    | /v1/books/${id}      | Action: show   |
 | GET    | /v1/books/${id}/edit | Action: edit   |
 | PUT    | /v1/books/${id}      | Action: update |
 | DELETE | /v1/books/${id}      | Action: delete |

Ответ 6

Все, что должно быть необходимо, это аннотация @Resource с uri в классе Domain. Если вам нужны определенные форматы (сначала формат по умолчанию), также включают в себя следующие форматы:

@Resource(uri='/books', formats=['json', 'xml'])

Это должно быть так. Если ypu по-прежнему не удается найти вашу динамическую конечную точку @Resource, попробуйте запустить:

grails url-mappings-report

Это даст вам хорошее резюме всех URL-адресов, включая те, которые поддерживаются контроллерами леса для доменов @Resource. Я обнаружил, что я стараюсь делать глупые ошибки, пытаясь "угадать" URL-адрес - используя вывод отчета, вы гарантируете, что вы и граалы согласны.