I'm using Spring boot 1.5.10. I have a RestController that handles file uploads, which I'm calling from another process using RestTemplate.
When the file I'm trying to upload is too big, Tomcat triggers a org.apache.tomcat.util.http.fileupload.FileUploadBase.SizeLimitExceededException (as expected). I want to handle it nicely by returning a custom HTTP response, but so far none of my attempts seem to matter in any way because restTemplate.exchange always throws the following exception:
org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://127.0.0.1:8080/api/upload": Software caused connection abort: recv failed; nested exception is java.net.SocketException: Software caused connection abort: recv failed
What I've tried so far:
- Setting up an ExceptionHandler for MultipartException and handling the special case for nested FileUploadBase.SizeLimitExceededException (as per Spring Boot handle SizeLimitExceededException): the handler is called but I still get the same exception
- Setting up an ErrorController Spring Boot Remove Whitelabel Error Page): the error handler is called but again I still get the same exception
What have I missed? Why is my handler's response ignored?
Update:
I tried the ExceptionHandler approach on Mac and it works as expected. Could this be Windows-specific ?
Here's some of my code (significant bits only):
Upload controller method:
@PostMapping("/upload")
public ResponseEntity<?> uploadFile(
@RequestParam("file") MultipartFile file
) throws IOException {
// do something with the file
return new ResponseEntity<>("Successfully uploaded '" + file.getOriginalFilename() +"'",
new HttpHeaders(), HttpStatus.OK);
}
RestTemplate call:
RestTemplate restTemplate = new RestTemplate();
URL url = new URL("http", this.targetHostname, this.targetPort, "/upload");
// build request
MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
map.add("file", new PathResource(path));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(map, headers);
// perform HTTP request
ResponseEntity<String> result;
try {
result = restTemplate.exchange(
url.toURI(),
HttpMethod.POST,
requestEntity,
String.class);
} catch (RestClientException e) {
log.error("Exception during file upload: ", e);
return;
}
log.debug("Result of upload: " + result);
Attempt with ExceptionHandler:
@ControllerAdvice
public class RestResponseEntityExceptionHandler {
@ExceptionHandler(MultipartException.class)
@ResponseBody
ResponseEntity<?> handleMultipartException(HttpServletRequest request, Throwable ex) throws Throwable {
Throwable cause = ex.getCause();
if (cause instanceof IllegalStateException) {
Throwable cause2 = cause.getCause();
if (cause2 instanceof FileUploadBase.SizeLimitExceededException) {
return new ResponseEntity<>(cause2.toString(), HttpStatus.PAYLOAD_TOO_LARGE);
}
}
throw ex;
}
}
Attempt with ErrorHandler:
@RestController
public class RestErrorController implements ErrorController {
private static final String PATH = "/error";
@RequestMapping(value = PATH)
public String error() {
return "Some error message";
}
@Override
public String getErrorPath() {
return PATH;
}
}