Question

I am trying to get a custom JSON renderer for exceptions working in my REST api.

I was able to get a custom marshaller working that did most of what I needed, but I would like to have control over the context that I don't have access to in the marshaller. The grails documentation shows how to write a custom renderer, and I have one that I think should work, but I can't use it during unit testing my REST controller.

Grails docs: http://grails.org/doc/2.3.4/guide/webServices.html#customRenderers

Does anyone know how I would initialize this renderer to be used in my controller actions during unit testing?

The above documentation only says how to set it up in the resources.groovy file.

When I was using the marshaller, I was able to do:

def setup(){
    //Set up the custom JSON marshallers
    JSON.registerObjectMarshaller(new CusomMarshaller(), 1)
}

But I don't see an equivalent method for Renderers. Can anyone point me in the right direction?


Further details:

Here is my renderer:

class JSONExceptionRenderer extends AbstractRenderer<Exception>{

JSONExceptionRenderer(){
    super(Exception, [MimeType.JSON, MimeType.HAL_JSON, MimeType.TEXT_JSON] as MimeType[])
}

@Override
void render(Exception object, RenderContext context) {
    log.warn("RENDERING")
    Exception exception = (Exception) object

    //Default to internal error
    Integer code = 500

    //If it is a defined exception with a more appropriate error code, then set it
    if(exception instanceof RestException){
        code = (Integer) ((RestException) exception).getCode()
    }else if(exception instanceof MissingResourceException){
        code = 404
    }
    context.status = HttpStatus.valueOf(code)

    //Write the JSON
    Writer writer = context.getWriter()
    Map content = ["code":code, "status":"error", "message":exception.message]
    JsonBuilder builder = new JsonBuilder(content)
    builder.writeTo(writer)

}
}

And this is the way I am trying to get it to work:

try{
  log.info "Throwing exception"
  throw new NullPointerException("Test Exception")
}catch(Exception ex){
  render ex as JSON
}

Thanks!

Was it helpful?

Solution

If you are using spock, you can inject the bean directly in Specification.

@TestFor(MyController)
class MyControllerSpec extends spock.lang.Specification {
     def myCustomRenderer //bean name used in resources.groovy

     //continue with tests
}

If you are using junit tests, then you can use defineBeans as:

@TestFor(MyController)
class MyControllerTests {
    void setup() {
         defineBeans {
            myCustomRenderer(com.example.MyCustomRenderer)
         }
    }

    //continue with tests
}

You can refer this answer as well for use of defineBeans.

I believe this is what you just need to do to test the behavior of the renderer.

OTHER TIPS

After much digging around in the source. I thought I would post this here for others.

The reason my custom renderer didn't work was you have to use the "respond" method on the controller:

respond object

That will check the class ControllersRestApi with a very large respond method to find your renderer in the rendererRegistry and use it. This is different than the object marshallers which use the render as notation.

In addition you need to also flush the writer, which wasn't in the orgininal documentaiton:

builder.writeTo(writer)
writer.flush()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top