Spring Boot: 3.3.13; 3.4.7
If return type in Controller is ResponseEntity
package com.example.demo;
import java.io.FileInputStream;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
@RestController
public class DefaultController {
@GetMapping("/")
public ResponseEntity<StreamingResponseBody> getMethodName() {
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"report.txt\"")
.body(out -> {
try (var in = new FileInputStream("C:\\template.txt")) {
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
}
});
}
}
package com.example.demo;
import java.io.FileNotFoundException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class DefaultExceptionHandler {
@ExceptionHandler
ResponseEntity<String> handle(FileNotFoundException e) {
return ResponseEntity.internalServerError()
.contentType(MediaType.TEXT_PLAIN)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; inline")
.body("File not found");
}
}
http request log
* Preparing request to http://localhost:8080/
* Current time is 2025-06-26T10:26:12.266Z
* Enable automatic URL encoding
* Using default HTTP version
* Enable timeout of 30000ms
* Disable SSL validation
* Hostname in DNS cache was stale, zapped
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#13)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: insomnia/11.0.2
> Accept: */*
* Mark bundle as not supporting multiuse
< HTTP/1.1 500
< Content-Disposition: attachment; filename="report.txt"
< Content-Disposition: attachment; inline
< Content-Type: text/plain
< Content-Length: 14
< Date: Thu, 26 Jun 2025 10:26:12 GMT
< Connection: close
* Received 14 B chunk
* Closing connection 13
I also attached demo project to reproduce it.
Comment From: rstoyanchev
Similar to #34366 but for "Content-Disposition".
Comment From: isanghaessi
hello! 👋 I am watching this issue now. I was debugging similar issue #35087. I read issue #34366 too.
It's a similar problem to the previous one I debugged, so I'm paying attention to it. I thought of two solutions to this problem. If possible, I would like to listen to @rstoyanchev 's opinion and handle this issue! Would you mind if I handle this issue??? 🙇♂️🙇♂️🙇♂️ I can follow up so quickly and get to work!
problem
it's because headers of the response that was making in controller method can be passed to exception resolver. it can be set any heaers in normal controller and can be passed to dispatcher servlet - exception resolver. in issue #34366, 'Contnet-Type' is special header so we can set to null before processing exception resolvers. -> it's gonna be set clearly.
I thought of two options to solve this problem
-
set all heaers 'null' before processing exception resolvers. I think the ideal ways to solve this problem is 'set every header to null before processing exception reosolvers. then exception resolver have responsibility to make whole response. But I don't know if this fits the design intent of exception resolver. So, I can't be sure all exception resolvers work well.
-
we can prevent all duplicated headers. we can add logic that makes a comparison before and after resolve exception to check what headers are will be duplicated and remove old headers. maybe here, DispatcherServlet.processHandlerException.