To render JSON+HAL I've added #Resource anotation to my domain:
#Resource(uri = "/rest/book", formats=['hal'])
class Book {
String title
String author
}
The problem is that I already have BookController (no scaffolding) and creating ling via gsp tag (<g:createLink controller='book' />) always creates link to /rest/book not to specyfic action in controller (i.e. /book/index). What is also worth knowing when I type localhost:8080/book/index it is showing JSON response not gsp page.
The #Resource somehow cover the book controller and I don't know how to keep both of them working.
PS I'm using Grails 2.4.4
Use namespaces for your controller.
class BookController {
static namespace = 'namespaceOne'
// …
}
and then use the namespace to generate links to your BookController.
<g:link controller="book" namespace="namespaceOne">Click me</g:link>
I have an ErrorController that handles various application errors such as 404, 500, etc. In these actions, I render appropriate error pages. I would like to be able to provide a link on these error pages that takes the user back to the page prior to receiving the error.
I've tried getting the HTTP_REFERER header but it is always null. request.forwardURI gives me the current request that is causing the error.
Suggestions?
class ErrorController {
def notFound() {
def backToPage = getPreviousPage // help here
render view: '404', mode: [backToPage: backToPage]
}
}
You can use history.back() method.
<script>
function goBack() {
window.history.back()
}
</script>
<input type="button" value="Back" onclick="goBack()">
Problem:
I am able to execute the default action(listLatest) of my Controller(ReviewMetricsController). But, I am not able to explicitly invoke other action(index) with form submission.
I am using Grails 2.0.4 and running my application in development mode from Eclipse IDE with Grails plugin installed.
Detail:
I have a form in my gsp as shown below
<g:form name="queryForm"
url="[controller:'reviewMetrics', action:'index']">
<g:actionSubmit value="submit" />
</g:form>
When I submit the above form, I get 404 error
The requested resource (/reviewmetricsapp/reviewMetrics/index) is not available
My Controller(reviewMetricsController.groovy) looks as shown below
package reviewmetricsapp
class ReviewMetricsController {
static scope = "session"
static defaultAction = "listLatest"
def gatherMetricsService
def grailsApplication
def latestMetrics
def index() {
render(view:"dashboard", model: [model: latestMetrics])
}
def listLatest(){
println grailsApplication.config.metricsapp.perlScript.loc
latestMetrics = gatherMetricsService.getLastWeekMetrics()
println "printing ${latestMetrics}"
render(view:"showMetrics", model: [metrics: latestMetrics])
}
}
And my urlMappings.groovy looks as shown below
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?"{ constraints {
// apply constraints here
} }
"/reviewMetrics/index"{
controller="reviewMetrics"
action="index"
}
"/"(view:"/index")
"500"(view:'/error')
}
}
Any help is appreciated
You do not need to change the URLMappings-file for your use-case. (remove the special case handling for reviewMetrics/index - its handled from the first rule)
Please use the following form definition:
<g:form name="queryForm"
controller="reviewMetrics"
action="index">
[..]
</g:form>
Please double check that your action is not accessed - simply put a plain index.gsp and do not care inside the controller. I guess the error is somewhere else.
Working in grails allows you to be quite DRY, but I still find myself frequently doing this:
def bookInstance = new Book(params)
if (!bookInstance.validate()) {
render(view: "create", model: [bookInstance: bookInstance])
}
else {
// stuff...
}
Is there a way to specify that a particular controller/action should bind a particular domain and then all the validation and view re-rendering happens before my action is executed? That way I only need to worry about //stuff....
You may use the Interceptors for validation, but interceptors cant render view.
I have the next scenario in my UrlMappings.groovy:
"/user/$action?" (controller:"user")
"/admin/$action?" (controller:"user")
"500"(controller:"error", action:"show")
"404"(controller:"error", action:"show")
And I need to know on the errorController from which controller was thrown the exception (if any) that raises the error 500, and show diferent error pages for users and admin.
Any ideas?
Thanks in advance.
You can access the exception in your ErrorController via request.exception. The top level exception always points to the controller where it was thrown so you can find out the controller name with exception.className. Here's a very simple example.
class ErrorController {
def show = {
def exception = request.exception
render(text: "Exception in ${exception?.className}",
contentType: "text/plain", encoding: "UTF-8")
}
}
Using request.getAttribute("exception") you'll have the exception at your hand. I'd take a look at all request attributes, maybe there's a direct reference to the originating controller.
UPDATE
The trick is that Grails wraps the thrown exception into a GrailsWrappedRuntimeException providing comfortable access to the code being responsible for the exception. Use the following snippet in your error controller:
import org.codehaus.groovy.grails.web.errors.GrailsWrappedRuntimeException
def action = {
def exception = request.getAttribute('exception')
if (exception instanceof GrailsWrappedRuntimeException) {
log.error "exception $exception.className, line $exception.lineNumber has throw $exception.cause"
}
}
To show different "error 500" page, I think you could do the same way with Grails scaffolding:
First, we just need to specify the view in URL Mapping:
"500"(view: "/500") // Point to 500.gsp
Then here's the "500" view code:
<h1>Grails Runtime Exception</h1>
<h2>Error Details</h2>
<div class="message">
<strong>Error ${request.'javax.servlet.error.status_code'}:</strong>
${request.'javax.servlet.error.message'.encodeAsHTML()}<br/>
<strong>Servlet:</strong> ${request.'javax.servlet.error.servlet_name'}<br/>
<strong>URI:</strong> ${request.'javax.servlet.error.request_uri'}<br/>
<g:if test="${exception}">
<strong>Exception Message:</strong> ${exception.message?.encodeAsHTML()} <br/>
<strong>Caused by:</strong> ${exception.cause?.message?.encodeAsHTML()} <br/>
<strong>Class:</strong> ${exception.className} <br/>
<strong>At Line:</strong> [${exception.lineNumber}] <br/>
<strong>Code Snippet:</strong><br/>
<div class="snippet">
<g:each var="cs" in="${exception.codeSnippet}">
${cs?.encodeAsHTML()}<br/>
</g:each>
</div>
</g:if>
</div>
<g:if test="${exception}">
<h2>Stack Trace</h2>
<div class="stack">
<pre><g:each in="${exception.stackTraceLines}">${it.encodeAsHTML()}<br/></g:each></pre>
</div>
</g:if>
You can extract whatever information you need from the error & stacktrace (div class="stack").
You can make 2 different template for user & admin, then a g:if tag will decide which template needs including in the view.
You can access the Controller instance that was handling the bad request via the request attribute org.codehaus.groovy.grails.CONTROLLER e.g. (in GSP):
Controller: ${request['org.codehaus.groovy.grails.CONTROLLER']}
To get the name of the controller:
Controller name: ${request['org.codehaus.groovy.grails.CONTROLLER_NAME_ATTRIBUTE']}
I tested this in Grails 2.0 and 2.2 but I can't find it documented anywhere, so this may be different in different versions of Grails. To see all attributes available in your request, add the following to error.gsp:
${${request.findAll { true }}
I sometimes annotate my controllers with a description that I'd like to show in the error message:
// ControllerType is a custom annotation
#ControllerType(description= "this does foo bar")
class MainController {
...
With that in place, and based on the post from Aoi Karasu, here's how to extract information from the initial Controller:
class ErrorsController {
def index() {
def initialController = request.exception?.className
if (initialController) {
def controller = grailsApplication.getArtefact("Controller", initialController).getReferenceInstance()
render "Controller: ${initialController}, annotations ${controller.getClass().getDeclaredAnnotations()}"
return
}
render 'no initial controller'
}
}
request.exception?.className combined with grailsApplication.getArtefact allows to retrieve the controller, from which you can, for example, extract annotations