Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Alternate grails controller template and url mapping for adding a scaffolded JSON API
package gvl
import grails.converters.JSON
class ErrorController {
// Forbidden
def error403() {
withFormat {
html { render(view: 'error403') }
json {
render(status: 403, text:'', contentType: 'application/json')
}
}
}
// Not Found
def error404() {
withFormat {
html {
//String missingPage = request.getAttribute('javax.servlet.error.message')
//log.info("404 when trying to access page ${missingPage}")
render(view: 'error404')
}
json {
render(status: 404, text:'', contentType: 'application/json')
}
}
}
// Conflict
def error409() {
withFormat {
html { render(view: 'error409') }
json {
render(status: 409, text:'', contentType: 'application/json')
}
}
}
def error500() {
def exception = request.exception
withFormat {
html { render(view: '/error') }
json { render exception as JSON}
}
}
}
<%=packageName ? "package ${packageName}\n\n" : ''%>import org.springframework.dao.DataIntegrityViolationException
import grails.converters.JSON
class ${className}Controller {
static allowedMethods = [list:'GET',
show:'GET',
edit:['GET', 'POST']
save:'POST',
update:['POST','PUT'],
delete:['POST','DELETE']]
def index() {
redirect(action: "list", params: params)
}
def list(Integer max) {
params.max = Math.min(max ?: 25, 100)
def list = ${className}.list(params)
withFormat {
html {
[${propertyName}List: list, ${propertyName}Total: ${className}.count()]
}
json {
render list as JSON
}
}
}
def create() {
[${propertyName}: new ${className}(params)]
}
def save() {
def ${propertyName} = new ${className}(params)
if (!${propertyName}.save(flush: true)) {
withFormat {
html {render(view: "create", model: [${propertyName}: ${propertyName}])}
json {
response.status = 400
render ${propertyName}.errors as JSON
}
}
return
}
withFormat {
html {
flash.message = message(code: 'default.created.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), ${propertyName}.id])
redirect(action: "show", id: ${propertyName}.id)
}
json {
response.status = 201
render ${propertyName} as JSON
}
}
}
def show(Long id) {
def ${propertyName} = ${className}.get(id)
if (!${propertyName}) {
withFormat {
html {
flash.message = message(code: 'default.not.found.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), id])
redirect(action: "list")
}
json {render(status: 404, text:'', contentType: 'application/json')}
}
return
}
withFormat {
html {[${propertyName}: ${propertyName}]}
json { render ${propertyName} as JSON }
}
}
def edit(Long id) {
def ${propertyName} = ${className}.get(id)
if (!${propertyName}) {
flash.message = message(code: 'default.not.found.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), id])
redirect(action: "list")
return
}
[${propertyName}: ${propertyName}]
}
def update(Long id, Long version) {
def ${propertyName} = ${className}.get(id)
if (!${propertyName}) {
withFormat {
html {
flash.message = message(code: 'default.not.found.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), params.id])
redirect(action:"list")
}
json {render(status: 404, text:'', contentType: 'application/json')}
}
return
}
if (version != null) {
if (${propertyName}.version > version) {<% def lowerCaseName = grails.util.GrailsNameUtils.getPropertyName(className) %>
${propertyName}.errors.rejectValue("version", "default.optimistic.locking.failure",
[message(code: '${domainClass.propertyName}.label', default: '${className}')] as Object[],
"Another user has updated this ${className} while you were editing")
withFormat {
html {render(view: "edit", model: [${propertyName}: ${propertyName}])}
json {render(status: 409, text:'', contentType: 'application/json')}
}
return
}
}
${propertyName}.properties = params
if (!${propertyName}.save(flush: true)) {
withFormat {
html {render(view: "edit", model: [${propertyName}: ${propertyName}])}
json {
response.status = 400
render ${propertyName}.errors as JSON
}
}
return
}
withFormat {
html {
flash.message = message(code: 'default.updated.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), ${propertyName}.id])
redirect(action: "show", id: ${propertyName}.id)
}
json { render ${propertyName} as JSON }
}
}
def delete(Long id) {
def ${propertyName} = ${className}.get(id)
if (!${propertyName}) {
withFormat {
html {
flash.message = message(code: 'default.not.found.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), id])
redirect(action: "list")
}
json {render(status: 404, text:'', contentType: 'application/json')}
}
return
}
try {
${propertyName}.delete(flush: true)
withFormat {
html {
flash.message = message(code: 'default.deleted.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), id])
redirect(action: "list")
}
json {render(status: 204, text:'', contentType: 'application/json')}
}
}
catch (DataIntegrityViolationException e) {
withFormat {
html {
flash.message = message(code: 'default.not.deleted.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), id])
redirect(action: "show", id: id)
}
json { response.sendError(500) }
}
}
}
}
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
name api0: "/api/$controller/$id"(parseRequest:true){
action = [GET: "show", PUT: "update", DELETE: "delete"]
constraints {
id(matches:/\d+/)
}
}
name api1: "/api/$controller"(parseRequest:true){
action = [GET: "list", POST: "save"]
}
name api2: "/api/$controller/$action"(parseRequest:true)
name api3: "/api/$controller/$action/$id"(parseRequest:true)
"/"(view:"/index")
"403"(controller: "error", action: "error403")
"404"(controller: "error", action: "error404")
"409"(controller: "error", action: "error409")
"500"(controller: "error", action: "error500")
"500"(controller: "error", action: "error403", exception: AccessDeniedException)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment