I fail to configure Spring correctly with static resources. I read many posts everywhere, found various but incomplete responses.
Context:
- Angular module (build with maven-frontend-plugin)
- Fronted embedded in the Spring fatjar
- Backend only handles requests starting by /api
- Backend also handles static resources from an external directory
- Backend do not have to know frontend paths
- Backend can use PathMatcher or AntMatcher (who cares ?!)
- Frontend uses real routes (not #)
- Frontend handles exactly /, /foo, /bar, for this example
- Java 17 & Angular 14
What we expect:
- For
/api/**
: let Spring handles the request without searching static resources and if the API does not exist, we expect a 404 in a JSON format. All answers fail to satisfy this rule. - For static resource from directory: read them if possible
- For all others URLs (we suppose the backend only handles /api) we use frontend static resources, and the frontend decided to display a 404 page when not found (or just redirect on /, whatever)
Solution 1: only update application.yml
spring:
web:
resources:
static-locations: classpath:/static/,file:/tmp
This is the simplest solution and we have:
- http://localhost:8080/: OK, serves frontend
- http://localhost:8080/static.png : OK, serves static file from directory
- http://localhost:8080/foo: KO, blank page (should serve frontend)
- http://localhost:8080/api/not-exist: KO, blank page (should be json 404)
To handle 404 on backend, we could catch Tomcat errors with a custom ErrorController (controllerAdvice + RestController) with @RequestMapping("${server.error.path:${error.path:/error}}")
but as soon as we are using this, we lost static resources from directory (static.png above).
Solution 2: solution 1 + overload WebMvcConfigurer
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry
.addViewController("/**/{path:[\\w]+}")
.setViewName("forward:/");
}
and
spring:
mvc:
throw-exception-if-no-handler-found: true
pathmatch:
matching-strategy: ANT_PATH_MATCHER
Better:
- http://localhost:8080/: OK, serves frontend
- http://localhost:8080/static.png : OK, serves static file from directory
- http://localhost:8080/foo: OK, serves frontend
- http://localhost:8080/api/not-exist: KO, redirects on frontend
Also, I still don't understand how it works, and the Spring order resolution. I understood Controller got the priority, ok, then what ? And if we add the ErrorController it do not catch any 404 anymore. And throw-exception-if-no-handler-found
seems to have not effect either.
Maybe we should add a context for external static resources. We could have:
- /api/...: use controllers or throw 404 as JSON
- /external/...: use external resources or 404 blank (or JSON)
- other: it serves frontend and this one manages 404