Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring - Unable To Send Error Message For API Returning ByteArrayResource

I have an rest API in a Spring for generating and downloading a PDF file. The controller definitation is as follows -

@RequestMapping(
        value = "/foo/bar/pdf",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
@ResponseBody
@Nullable
public ByteArrayResource downloadPdf(@RequestParam int userId) {
    byte[] result = null;
    ByteArrayResource byteArrayResource = null;

    result = service.generatePdf(userId);

    if (result != null) {
        byteArrayResource = new ByteArrayResource(result);
    }

    return byteArrayResource;
}

I use Jackson for JSON handling JSON and have an Exception handler ControllerAdvice. The problem is when this API generates an exception and I return a custom exception class (contains message and one additional field).

As I already specified produces = MediaType.APPLICATION_OCTET_STREAM_VALUE this custom class is also attempted to be converted to an octet stream by Spring, which it fails at and produces HttpMediaTypeNotAcceptableException: Could not find acceptable representation.

I tried solutions on this Stackoverflow question, particularly this answer but it still fails. This solution, along with other changes suggests removing produces part from @RequestMapping but when I debugged into AbstractMessageConverterMethodProcessor.getProducibleMediaTypes it only detects application/json as available response media type.

tl;dr How can I have this API return the file on success and correctly return custom exception class's JSON representation on error.

like image 489
Harshil Sharma Avatar asked Sep 19 '25 07:09

Harshil Sharma


1 Answers

I had the same problem with similar code. I just removed the produces attribute from my @PostMapping and I was able to return the file or the json (when the api have some error):

@Override
@PostMapping
public ResponseEntity<InputStreamResource> generate(
        @PathVariable long id
) {
    Result result = service.find(id);

    return ResponseEntity
            .ok()
            .cacheControl(CacheControl.noCache())
            .contentLength(result.getSize())
            .contentType(MediaType.parseMediaType(MediaType.APPLICATION_PDF_VALUE))
            .body(new InputStreamResource(result.getFile()));
}

When some error occur, I had a @ExceptionHandler to care of that:

@ExceptionHandler
public ResponseEntity<ApiErrorResponse> handleApiException(ApiException ex) {
    ApiErrorResponse error = new ApiErrorResponse(ex);
    return new ResponseEntity<>(error, ex.getHttpStatus());
}
like image 96
Dherik Avatar answered Sep 22 '25 06:09

Dherik