Question

Is there an exception or something else that can be raised from a service (or other non-Controller method) that will interrupt the current response handling and return a 404 to the user?

In the Django world, there is get_object_or_404 which raises a Http404 exception which has this effect. I'm writing service; if that service determines that the object requested is not accessible to the user (in this case it's not published yet), I would like it to trigger a 404 and stop the remainder of the execution. The goals is for the controllers calling the service to be DRY and not always repeat the def obj = someService.getSomething(); if (obj) {} else { render status: 404} call.

Summary:
In Django, I can raise a Http404 at any point to stop request processing and return a 404. Is there an equivalent or a way to do this in Grails NOT from a controller?

Était-ce utile?

La solution

Create an Exception class like com.my.ResourceNotFoundException and then throw it from whatever place you want (controller or service).

Create a controller like the one below:

class ErrorController {

    def error404() {
        response.status = HttpStatus.NOT_FOUND.value()
        render([errors: [[message: "The resource you are looking for could not be found"]]] as JSON)
    }
}

Then add into your UrlMappings.groovy configuration file an entry that will handle exceptions of that type with this controller action. Specifying "500" as the pattern means that it will catch 500 errors (like the one your thrown ResourceNotFoundException will cause) and if the exception matches that type it will use the controller and action specified.

"500"(controller: "error", action: "error404", exception: ResourceNotFoundException)

Autres conseils

In a controller, you can set the status of the response to any code you like.

    response.status = 404

You can also use render with a status - from the docs:

// render with status code
render(status: 503, text: 'Failed to update book ${b.id}')

You can have your controller that delegates to the service do this prior to calling the service method, or you can have your service return a status code to the controller.

@doelleri has mentioned what can be done exactly for rendering status code.

A "not-so-groovier" way you can achieve DRYness in controller is shown below. But again if you want to move the try catch block to an utility you can achieve something more.

//Custom Exception
class CustomException extends Exception{
    Map errorMap

    CustomeException(Map map, String message){
        super(message)
        this.errorMap = map
    }
    ....
}

//service
def getSomethingGood(){
    ....
    ....
    if(something bad happened){
        throw new CustomException([status: HttpStatus.NOT_FOUND.value, 
                                   message: "Something really got messed up"], 
                                   "This is a custom Exception")
        //or just return back from here with bad status code 
        //and least required data
    }
    ......
    return goodObj
}

def getSomething(){
    def status = HttpStatus.OK.value
    def message
    try{
        def obj = getSomethingGood()
        message = "success value from obj" 
        //or can also be anything got from obj  etc
    } catch(CustomException c){
        status = c.errorMap.status
        message = c.errorMap.message
    }

    [status: status, message: message]
}

//controller
def obj = someService.getSomething()
render(status: obj.status, text: obj.message)

Also note, when you handle checked exceptions, transactions do not roll back in service layer. There is something else that has to be done, which I think is out of scope of this question.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top