How to know from where was thrown error 500 (Grails) - grails

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

Related

Grails display data from database

I'm creating a Grails app following this tutorial: http://grails.asia/grails-tutorial-for-beginners-display-data-from-the-database
When I try to display data from database though, nothing happens. Below is my controller code:
def index(){
def users = User.list();
[users:users]
}
def displayUsers(){
if(User.count()==0){
render 'User list is empty!';
}else {
render(view: 'userlist.gsp');
//a little test
println User.list().size();
println User.count();
for(User u : User.list()){
println u.username;
}
}
}
The User.count() is working, because I can see the usernames in console and userlist.gsp renders every time, but it seems that my view code doesn't see the list. Here's the userlist.gsp code:
<body>
userlist.gsp
<g:each in="${users}" var="user" status="i">
<h3>${i+1}. ${user.username}</h3>
</g:each>
</body>
What can be wrong with this code? I've been making precisely the same steps as in the tutorial above in my analogical app, but it doesn't seem to work. This is especially weird, since I've found a similar question under this link: grails: show list of elements from database in gsp
and it's been marked as accepted answer. Why does exacly the same way not work in my app?
render gsp view as follows (pass data as a model).
render(view: "userlist", model: [userList : User.list()])
Now get model data in gsp as follows.
<body>
userlist.gsp
<g:each in="${userList}" var="user" status="i">
<h3>${i+1}. ${user.username}</h3>
</g:each>
</body>
You can do with many options:
rename your gsp action in controller from def index() to def userlist()
or rename index.gsp file to userlist.gsp
you can redirect to userlist.gsp with ${users} object
So change in controller def index() action
[users:users] to redirect(action: "userlist", params: ["users": users])
Note: 2nd method will show parameters in url.
Refer Grails Doc
You can use chain
e.g. chain(action: "userlist", model: ["users": users])
Refer Grails Doc
every gsp action (page) needs to be injected

Error handling using controller in grails

I have an error.gsp file that handles errors. I want to add server logic to the errors behaviour so I changed my mappings
from: "500"(view: '/error')
to: "500"(controller: 'errors')
in my UrlMappings class.
And added a new controller:
class ErrorsController {
def index() {
println(request.exception)
render view: '/error'
}
}
I need the exception data and it is NULL, yet it exists in the view.
The model is also null.
How can I get the exception object in the errors controller?
Regards, Ido

I want to provide a link to return the previous page on a 404

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()">

Controller is there, but showing "Error code 404 page not found" in grails

new.gsp:
<html>
<head>
</head>
<body>
<g:form name="myForm" controller="New" action="index" params="username:username">
<div>
<fieldset class="form">
<label for="name">Name:</label>
<div>
<g:textField name="username" value="${params.userName}"/>
</div>
</fieldset>
</div>
<g:submitButton name="Submit" value="Submit" />
</g:form>
</body>
<html>
NewController.groovy:
package sample
import com.sun.org.apache.bcel.internal.generic.NEW;
class NewController {
def index = {
if($params.userName){
render(view:"/login.gsp")
}
}
}
login.gsp is a simple page, having a simple welcome note.
if some body the solution please reply,
thanks in advance.
by prasanth
Change your controller name to "new" instead of New in
It will work.
Or else you can modify your "save" action in controller, so that when you click on save button new page will be rendered.
There are a few issues in the posted code that will cause you problems:
You're accessing the parameters with $params instead of params. The $ character is only necessary when you are in a GString. e.g. def foo = "your username is ${params.userName}"
Your view is named new.gsp but your action is named index. Grails will by default look for a view matching the action name in a directory named for the controller. In other words, since you don't tell it explicitly to render /new.gsp grails will look for /new/index.gsp. You can either rename the view to /new/index.gsp or tell grails to render the view new in the index action.
When attempting to render your logged in page, you're calling render(view: 'login.gsp'). The gsp extension is not necessary when calling the render tag. You're intended to use the grails view name, not the filename. render(view: 'login')
If you're using a recent version of grails (>2.0) you should be using controller methods rather than closures. e.g. def actionName() { } as apposed to def actionName() = { }. The reasoning is in the grails documentation.
Here's what it could look like with all the issues addressed:
rename new.gsp to /new/index.gsp. rename login.gsp to /new/loggedIn.gsp.
controller:
class NewController {
def index() {
if (params.userName) {
forward action: 'loggedIn'
return // render and forward don't end flow control
}
}
def loggedIn() {} // no logic, automatically renders '/new/loggedIn.gsp'
}
Add a handler to your controller named login.
def login = {}
If the view file is new.gsp then you need your action to also be new or else have a URL mapping (in UrlMappings.groovy) to do something like:
"/new" {
controller = 'new'
action = 'new'
}
Or you can set
static defaultAction = 'new'
...in your NewController.
Then Grails will find the appropriate action on your controller.
if your action is called index, you can acces the page on
localhost:8080/webapp/NewController

Unable to decode the urlMapping in Grails application

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.

Resources