How to handle grails exceptions in an ajax request? - grails

If I am making a regular request I can define handling of errors and exceptions in UrlMappings.groovy as in the following example:
"403"(controller: 'error', action: 'index', params:['code':'403'])
"404"(controller: 'error', action: 'index', params:['code':'404'])
"500"(controller: 'error', action: 'index', params:['code':'500'])
"500"(controller: 'home', action: 'noPrivileges', exception: IllegalOperationException)
I can then handle the errors in a specific controller and render a view of my choice e.g.
class ErrorController {
def index = {
render view: "/errors/myErrorPage", model: [code: params.code]
}
}
This works perfectly and whenever there is an error on server I get my error page.
Now how can I achieve the very same behavior for ajax requests? I am making ajax requests either with grails remoteFunction or formRemote. Everytime when an exception occurs on the server, I want to execute the same piece of code in javascript (e.g. I want to show an alert with alert('There was an exception')).
The above described approach does not work for ajax requests. I still get back an error and even though there is content of myErrorPage.gsp in the error, it is not printed into the html element which I specified with update parameter of formRemote or remoteFunction.

I finally found a perfectly clean solution compatible with the approach which is used for handling standard requests.
You need to modify action which is handling the error as in the following example:
class ErrorController {
def index = {
if (request.xhr) {
response.status = 200
render template: "/errors/myAjaxErrorTemplate", model: [code: params.code]
} else {
render view: "/errors/myErrorPage", model: [code: params.code]
}
}
}
With request.xhr you can differentiate between ajax request and a standard request. In ajax request you further need to tell grails that the response is OK with setting response status to 200.
Very nice and clean. :) I am surprised this can't be googled anywhere...

The remoteFunction trigger a couple of events onFailure and on_ERROR_CODE. You can use that events to show your warning messages.
You can see more details in docs

If the request status is set correctly you could define an JavaScript function via the onFailure attribute of remoteFunction/formRemote.
Within this function you could handle your exception on client side.
Using the sample from the docs it could look like this:
<select onchange="${remoteFunction(action: 'bookByName',
update: [success: 'great', failure: 'ohno'],
params: '\'bookName=\' + this.value'),
onFailure: 'handleError'}">
<option>first</option>
<option>second</option>
</select>

Related

Grails `respond` returns null

I want to send validation errors back to a different page (add), so I have this for my save action:
#Transactional(readOnly = false)
def save(AddDomainCommand command) {
if (command.validate() && session.isLoggedIn && session.publisher) {
// do some stuff
return redirect(controller: 'Widget', action: 'generate')
}
log.info("Validation failed for $command")
respond view: "add", model: [domain: command]
}
It errors with javax.servlet.ServletException: Could not resolve view with name 'save' in servlet with name 'grailsDispatcherServlet'
If I print the response from respond, I get null! So that explains why it's going to save, because that's the convention for the action's name.
I need it to go back to the view it came from (add.gsp), yet grails respond is null and thus defaulting to save.gsp.
Any ideas?
respond uses a different syntax and is used when you want to be able to support multiple client types based on mime type, e.g. JSON and/or XML for a REST client and HTML/GSP for a regular browser client. If you just want to use add.gsp to render HTML, use render:
render view: 'add', model: [domain: command]

Transactional service causing ErrorController to Render instead of desired view

I've inherited a grails project in which an Error Controller is used.
Within the URLMappings there is the following entry:
"500"(controller: 'error', action:'error')
The specific errorController renders a specific view:
render view:'/error/prodError'
What I don't understand is how to bypass error controller for 500 errors and send to view /play/play but without removing the old one for other situations.
Even though I catch exceptions from the playService, I still get /error/prodError instead of /play/play.
I've debugged the code and seen that the render method is called twice. It is called once to go to /play/play but another time for /error/prodError when an exception is thrown in the service and the transaction is rolled back.
See playController excerpt below:
PlayController
try{
playService.play(parame:params} //Runtime exception thrown from playService.
//play is transactional
}
catch(Throwable t){
//Why isn't /play/play rendered?
//How do I pass errors to playservice for alert rendering?
render view: '/play/play',
model: [
domain: domain,
customer: customer,
game: game]
return
}
Update
Specifically, the errorController is entered because of an UnexpectedRollbackException that is the result of the rollback.
SO: How would someone go about not entering the ErrorController for a specific type of Exception that results from a specific Controller?
If you don't want to handle the error within a controller, you could render your (or any other) view with the following url mapping:
"500"(view:'/play/play')
If you need to handle individual exception you do it like this:
static mappings = {
.
.
.
"500"(controller: "error", action: "unexpectedRollback", exception: UnexpectedRollbackException)
"500"(controller: "errors", action: "nullPointer", exception: NullPointerException)
.
.
"500"(controller: 'error', action:'error')
}
As mentioned in docs you should avoid throwning exceptions from your error controller due to StackOverflowExceptions. I think it's not possible to divide between two exceptions of the same type but thrown from diffent controller.
If you handle the exception within your error controller you could try to set the response code directly. So - the url mapping may not handle your already handled exception.

Grails databinding and allowed HTTP methods

I have the following action in a controller
class RegisterController {
static allowedMethods = [register: 'POST']
def register(User user) {
// action body omitted
}
}
If a user tries to invoke this action via GET /register/register/newUser, I get a databinding failure because newUser cannot be bound to the user's Long id property.
This seems reasonable at first, however IMO no attempt should ever be made to bind the user when it is invoked via HTTP GET, because I've declared that only POST is allowed.
Curiously, if I change the action to:
class RegisterController {
static allowedMethods = [register: 'POST']
def register() {
User user = new User(params)
}
}
and again try to invoke it with GET /register/register/newUser, then I get the expected HTTP method not allowed (405) error. It seems to me that databinding is happening before the HTTP request type is checked, and this is why I get a binding error in the first case and a 405 error in the second.
Shouldn't I get a 405 in both cases?
Yes, you should get a 405 in both cases. File a report at https://jira.grails.org/browse/GRAILS and we will get it straightened out. We have an AST transformation which adds the command object handling code and adds the allowedMethods handling code. It sounds like they may not be in the right order. Will get it straightened out. Thanks for the feedback.

What triggers Grails HTTP status-based exception mapping?

If I have a UrlMappings.groovy that specifies:
"403"(controller: 'error', action: 'status403')
... then I was hoping to trigger this route at runtime by assigning:
response.status = 403
from within a controller or filter. But that doesn't happen. Is there a similarly declarative way for me to "kick a request over" to 403 handling from within a controller or filter?
response.sendError(403)
or
render(status: 403)
See your other question.

Does Grails allow Declarative Exception Handling with Filters?

I'd like to create an AuthorizationFilter in my Grails app that will inspect request parameters and determine whether a user is authorized to proceed with a request. I was hoping I could throw a custom exception from within my AuthorizationFilter and later handle it with Grails declarative execption handling via a route like:
"403"(controller: 'error', action: 'status403', exception:AuthException)
... but turns out when I try this in grails 2.2.4 (and the latest dev snapshot) I get
java.lang.IllegalArgumentException: Method name must not be null
at grails.plugin.cache.web.ProxyAwareMixedGrailsControllerHelper.retrieveAction(ProxyAwareMixedGrailsControllerHelper.java:41)
So... is there any good way to use declarative exception handling together with filters?
I guess you can handle declarative exception from Controllers but Filters. You can use response instead to send the error code
class AuthorizationFilters {
def filters = {
auth(controller: 'error', invert: true) {
before = {
if(1){ //If auth fails
response.sendError(403)
//render(status: 403) //or
//redirect(controller: 'error', action: 'status403') //or
}
return false
}
}
}
}
Above logic will render the response from ErrorController's status403 action based on the UrlMapping that is provided in the question
Make sure you are excluding error controller from filters.

Resources