Spring Boot如何根据浏览器User-Agent动态分发ES5/ES6静态资源?
Got it, let's work through this dynamic resource routing for your Spring Boot + Polymer app. You've got ES5/ES6 bundles split into /static/es5 and /static/es6, and you want to serve the right version based on the user's browser without changing the URL. Here's how to make this happen properly:
First, update your application.properties to point to the root static directory instead of hardcoding ES5. This lets Spring access both ES5 and ES6 bundles:
# Replace your old static-locations line with this spring.resources.static-locations=classpath:/static/
This removes the hardcoded ES5 fallback and gives us flexibility to route requests dynamically.
This is the cleanest, Spring-native way to handle dynamic static resource routing—it integrates directly into Spring's resource processing chain, no extra controllers or messy forwards needed.
Create a configuration class that implements WebMvcConfigurer and defines a custom resource resolver:
import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.resource.AbstractResourceResolver; import org.springframework.web.servlet.resource.ResourceResolverChain; import javax.servlet.http.HttpServletRequest; import java.util.List; @Configuration public class BrowserAwareResourceConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**") // Match all static resource requests .addResourceLocations("classpath:/static/") .resourceChain(true) .addResolver(new BrowserVersionResourceResolver()); } // Inner class to handle browser-specific routing private static class BrowserVersionResourceResolver extends AbstractResourceResolver { // UA patterns for target browsers private static final String IE11_UA = "Trident/7.0"; private static final String CHROME_UA = "Chrome/"; private static final String FIREFOX_UA = "Firefox/"; @Override protected Resource resolveResourceInternal( HttpServletRequest request, String requestPath, List<? extends Resource> locations, ResourceResolverChain chain ) { String userAgent = request.getHeader("User-Agent"); String resourcePrefix = "/es5/"; // Default to ES5 for safety // Route based on UA if (userAgent != null) { if (userAgent.contains(CHROME_UA) || userAgent.contains(FIREFOX_UA)) { resourcePrefix = "/es6/"; } else if (userAgent.contains(IE11_UA)) { resourcePrefix = "/es5/"; } } // Rewrite the request path to use the correct ES version prefix String resolvedPath = resourcePrefix + requestPath; return new ClassPathResource(resolvedPath); } @Override protected String resolveUrlPathInternal( String resourcePath, List<? extends Resource> locations, ResourceResolverChain chain ) { // We don't need to handle URL path resolution here since we directly resolve the resource return null; } } }
How This Works:
- The resolver checks the incoming request's
User-Agentheader - Routes Chrome/Firefox requests to
/es6/resources - Routes IE11 requests to
/es5/resources - Falls back to ES5 for unknown browsers to ensure maximum compatibility
- Use Chrome/Firefox to visit your app, open the DevTools Network tab, and verify resources load from the
/es6/directory (even though the URL shows the root path like/app.js) - Use IE11 to visit, and confirm resources load from
/es5/
If you want a simpler (though less elegant) implementation, use an interceptor to forward requests to the correct directory:
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Configuration public class BrowserRoutingInterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new BrowserRoutingInterceptor()); } private static class BrowserRoutingInterceptor implements HandlerInterceptor { private static final String IE11_UA = "Trident/7.0"; private static final String CHROME_UA = "Chrome/"; private static final String FIREFOX_UA = "Firefox/"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String userAgent = request.getHeader("User-Agent"); String requestUri = request.getRequestURI(); // Skip if already routed to ES5/ES6 to avoid infinite loops if (requestUri.startsWith("/es5/") || requestUri.startsWith("/es6/")) { return true; } String prefix = "/es5/"; if (userAgent != null) { if (userAgent.contains(CHROME_UA) || userAgent.contains(FIREFOX_UA)) { prefix = "/es6/"; } else if (userAgent.contains(IE11_UA)) { prefix = "/es5/"; } } // Forward to the correct ES version directory request.getRequestDispatcher(prefix + requestUri.substring(1)).forward(request, response); return false; } } }
- UA Accuracy: IE11 uses
Trident/7.0in its UA string (notMSIE), so don't rely on outdated patterns - Default Fallback: Always default to ES5 for unknown browsers to avoid breaking compatibility
- Cleanup: Make sure to remove your old
spring.resources.static-locationsline pointing directly to ES5
内容的提问来源于stack exchange,提问作者Junaid Akhtar

