0

EDIT: The error was in the client not the server. The response body was getting written, but the client was not reading it on a 400 response.

I have a custom message converter to produce text/csv, application/csv from an ErrorResponse object. It works as expected when the ErrorResponse is returned directly from a @RequestMapping annotated method, but returns no response body when ErrorResponse is return from an @ExceptionHandler annotated method in a @ControllerAdvice object. I have verified that the message converter writerInternal method is being called and is writing to the response body, but is never makes it back to the client.

ErrorResponse:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="response")
public class ErrorResponse {
    private String statusCode;
    private String userMessage;
    private String developerMessage;

    public String getStatusCode() {
        return statusCode;
    }

    public void setStatusCode(final String statusCode) {
        this.statusCode = statusCode;
    }

    public String getUserMessage() {
        return userMessage;
    }

    public void setUserMessage(final String userMessage) {
        this.userMessage = userMessage;
    }

    public String getDeveloperMessage() {
        return developerMessage;
    }

    public void setDeveloperMessage(final String developerMessage) {
        this.developerMessage = developerMessage;
    }

    public ErrorResponse() {
        super();
    }

    public ErrorResponse(final String statusCode, final String userMessage, final String developerMessage) {
        super();
        this.statusCode = statusCode;
        this.userMessage = userMessage;
        this.developerMessage = developerMessage;
    }

}

MessageConverter:

public class ErrorResponseCsvMessageConverter extends AbstractHttpMessageConverter<ErrorResponse> {

    public ErrorResponseCsvMessageConverter() {
        super(new MediaType("application", "csv", Charset.forName("UTF-8")),
                new MediaType("text", "csv", Charset.forName("UTF-8")),
                MediaType.TEXT_PLAIN);
    }

    @Override
    protected ErrorResponse readInternal(final Class<? extends ErrorResponse> clazz, final HttpInputMessage httpInputMessage)
            throws IOException, HttpMessageNotReadableException {

        // not supported

        return null;
    }

    @Override
    protected boolean supports(final Class<?> clazz) {
        return ErrorResponse.class.isAssignableFrom(clazz);
    }

    @Override
    protected void writeInternal(final ErrorResponse errorResponse, final HttpOutputMessage httpOutputMessage)
            throws IOException, HttpMessageNotWritableException {

        System.out.println(errorResponse);

        try(CSVWriter csvWriter = new CSVWriter(new OutputStreamWriter(httpOutputMessage.getBody(), "UTF-8"))) {
            csvWriter.writeNext(new String[] { "statusCode", "userMessage", "developerMessage" });
            csvWriter.writeNext(new String[] {
                    errorResponse.getStatusCode(),
                    errorResponse.getUserMessage(),
                    errorResponse.getDeveloperMessage() });
        }
    }

}

Controller Advice:

...
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseBody()
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorResponse handleMissingParamterException(final HttpServletRequest request, final HttpServletResponse httpServletResponse, final MissingServletRequestParameterException e) {
        LOG.warn("Bad Request:" +
                request.getRequestURI() +
                ((request.getQueryString()==null) ? "" : "?" + request.getQueryString()));

        return new ErrorResponse(
                "400",
                "There was an error with the request.",
                "Required parameter '" + e.getParameterName() + "' is missing.");
    }
...
Brian
  • 451
  • 4
  • 15
  • I can add a response header. ((ServletServerHttpResponse) httpOutputMessage).getServletResponse().setHeader("foo", "bar"); – Brian Jan 30 '15 at 17:16
  • I was only using `HttpServletResponse` for debugging purposes. The problem exists with or without it. – Brian Jan 30 '15 at 17:50
  • could you: tell wich spring version you're using? jackson version? jaxb? could you list here request/response HTTP headers as well? – Brian Clozel Feb 01 '15 at 16:00

1 Answers1

1

I think the message is being written but not flushed...

So your converter may be missing something like:

outputMessage.getBody().flush();

Maybe even use Spring's AbstractHttpMessageConverter ?

Brian Clozel
  • 56,583
  • 15
  • 167
  • 176
  • Adding the flush has had no effect. I am extending `AbstractHttpMessageConverter` which is providing the `writeInternal` method I override. – Brian Feb 01 '15 at 12:29