Official Grails documentation says that
Version 2.0.x of the scaffolding plugin includes different scaffolding templates that are aligned with the new REST APIs introcued in Grails 2.3 and above. (taken from here http://grails.org/doc/latest/guide/scaffolding.html)
But I can't make (or I don't understand the concept) work RESTfulness together with scaffolding.
Let's start from scratch:
grails create-app myapp
cd myapp/
grails create-domain-class Book
grails create-scaffold-controller myapp.Book
Add a field to the domain class
class Book {
String text
static constraints = {
}
}
and run the app with grails run-app
.
Surfing on the http://localhost:8080/myapp/
shows that scaffolding works great:
http://localhost:8080/myapp/book/index
page shows books listhttp://localhost:8080/myapp/book/show/1
page show details for the book with id = 1http://localhost:8080/myapp/book/create
page creates a bookLet's see what about REST.
Official docs say I should use URLs like http://localhost:8080/myapp/books/...
for the REST but any attempt to access the app, like this curl -i -H "Accept: application/json" localhost:8080/myapp/books/1
returns 404 with bunch of HTML.
Ok, let's read docs carefully:
The easiest way to create a RESTful API in Grails is to expose a domain class as a REST resource. This can be done by adding the grails.rest.Resource transformation to any domain class
No problem, now the Book class heading is
import grails.rest.*
@Resource(uri='/books') class Book {
Now surfing on the http://localhost:8080/myapp/
shows that scaffolding is broken:
http://localhost:8080/myapp/book/index
page shows books listhttp://localhost:8080/myapp/book/create
page shows xml output <?xml version="1.0" encoding="UTF-8"?><book><text /></book>
I'd played with @Resource and "/books"(resources:"book") in URLMappings.groovy but hadn't found any working solution which makes possible scaffolding and RESTfulness work back-to-back. Indeed, I managed to make them work separately.
Update
I'd found the way how to achieve the desired goal. The way I found is:
@Resource(uri = "/books")
.class HumanBookController {static scaffold = Book}
Now scaffold GUI pages with URLs like http://localhost:8080/myapp/humanBook/index
work pretty well. Either json requests are handled well with URLs like http://localhost:8080/myapp/books/1
. But it's not elegant to have 2 controllers doing same things for common web and json.
You can do this:
import grails.rest.RestfulController
class BookController extends RestfulController {
static responseFormats = ['html', 'json']
BookController() {
super(Book)
}
}
And then in the UrlMappings.groovy:
"/books"(resources:"book")
"/$controller/$action?/$id?(.${format})?"{
constraints {
// apply constraints here
}
}
No need to add @Resource in the domain.
You can now have /books/1.json or /books/1.html to point to the right places. You might still need to do grails generate-view Book
to have the view generated. But although you need to generate the views for html, you keep only single controller and path.