I have a Spring Boot
web application where I catch my custom exceptions in ControllerAdvice
class. The problem is that Spring Boot
doesn't throw exception by default if no handler is found (it sends json
back to a client).
What I want is to catch NoHandlerFoundException
in my ControllerAdvice
class. To make this possible I explicitly configured
spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
This trick does the job and I can catch NoHandlerFoundException
now but it disables Spring
to auto-configure path to static resources. So all my static resources are not available for a client now. I tried to resolve this using one more configuration which doesn't help
spring.resources.static-locations=classpath:/resources/static/
Could anybody please advise how to map static resources in Spring Boot
when auto-configuration was disabled with spring.resources.add-mappings=false
?
Thanks!
If your static resources are limited to specific URL paths, you can configure only those paths to be handled by the Spring static resources handler. In this example, the /doc
URL path is served by static resources in the /resources/static/doc/
folder in the classpath:
spring.mvc.static-path-pattern=/doc/**
spring.resources.static-locations=classpath:/resources/static/doc/
You'll need to remove this configuration:
spring.resources.add-mappings=false
I experienced the same issue and after some research, I found out that it is obviously not possible to have both options enabled (i.e. throwing NoHandlerFoundException
by setting spring.mvc.throw-exception-if-no-handler-found=true
AND serving static resources automatically).
Enabling the option to throw NoHandlerFoundException
requires one to set spring.resources.add-mappings
to false
, otherwise it would not work. Furthermore, in my test setup it was not possible to disable spring.resources.add-mappings
and specify the URLs for static resources manually (e.g. via application properties spring.mvc.static-path-pattern
and spring.resources.static-locations
or programmatically by overriding public void addResourceHandlers(ResourceHandlerRegistry registry)
), because then the spring.resources.add-mappings=false
setting seems to be overruled.
Finally, I implemented the following workaround for serving static resources manually via my own controller implementation:
@Controller
public class StaticWebContentController {
private Map<String, byte[]> cache = new HashMap<String,byte[]>();
@RequestMapping(value = "/css/{file}", method = RequestMethod.GET)
public ResponseEntity<byte[]> getCssFile(@PathVariable("file") String name){
ResponseEntity<byte[]> responseEntity = loadResource(".\\static\\css\\"+name,"text/css");
return responseEntity;
}
@RequestMapping(value = "/img/bootstrap-icons-1.1.0/{file}", method = RequestMethod.GET)
public ResponseEntity<byte[]> getimgFile(@PathVariable("file") String name){
ResponseEntity<byte[]> responseEntity = loadResource(".\\static\\img\\bootstrap-icons-1.1.0\\"+name,"image/svg+xml");
return responseEntity;
}
@RequestMapping(value = "/js/{file}", method = RequestMethod.GET)
public ResponseEntity<byte[]> getJsFile(@PathVariable("file") String name){
ResponseEntity<byte[]> responseEntity = loadResource(".\\static\\js\\"+name,"text/javascript");
return responseEntity;
}
private ResponseEntity<byte[]> loadResource(String path, String contentType){
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add("Content-Type", contentType);
if(hasCachedContent(path)){
return new ResponseEntity<byte[]>(getCachedContent(path),responseHeaders,HttpStatus.OK);
}else{
Resource resource = new ClassPathResource(path);
if(resource.exists()){
try{
InputStream inputStream = resource.getInputStream();
byte[] content = inputStream.readAllBytes();
putCache(path, content);
return new ResponseEntity<byte[]>(content,responseHeaders,HttpStatus.OK);
}catch(IOException e){
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR,e.getMessage());
}
}else{
throw new ResponseStatusException(HttpStatus.NOT_FOUND,"The requested resource '"+path+"' does not exist'");
}
}
}
private byte[] getCachedContent(String path){
return cache.get(path);
}
private boolean hasCachedContent(String path){
return cache.containsKey(path);
}
private void putCache(String path, byte[] content){
cache.put(path, content);
}
}
In my application, I have three types of static resources located in three different sub folders. Each type is handled by a separate endpoint in order to set the Content-Type
header properly. Moreover, the controller caches each resource in order to avoid to reload the requested resource from hard disk again.
Probably, this is not the best solution, however, a feasible workaround in case of my application. Any recommendations for improvement are highly appreciated!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With