I have a RESTful spring based endpoint to get assets stored in a db to a javascript editor. The relevant parts boil down to:
@RestController
@RequestMapping(ThemeEndpoint.ENDPOINT_NAME)
public class ThemeEndpoint {
public static final String ENDPOINT_NAME = "/themes";
@RequestMapping(value="/{id}/css/{assetName:.*}", method=RequestMethod.GET)
public Asset getCssItem(
@PathVariable("id") ThemeId id,
@PathVariable("assetName") String name) {
CssThemeAsset themeAsset = themeService.getCssAsset(
id, ThemeAssetId.fromString(name));
Asset asset = new Asset();
asset.name = themeAsset.getName();
asset.text = themeAsset.getContent();
return asset;
}
This works as expected for urls like
http://localhost:8080/app-url/rest/themes/ac18a080-a2f1-11e3-84f4-600308a0bd14/css/main.less
but fails as soon as i change the extension to .css
.
After some debugging i'm quite sure the request is not even mapped if i use an url like
http://localhost:8080/app-url/rest/themes/ac18a080-a2f1-11e3-84f4-600308a0bd14/css/main.css
With a high log level i can see that the mapping is catched by spring:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
- Mapped "{[/themes/{id}/css/{assetName:.*}],methods=[GET],params=[],headers=[],
consumes=[],produces=[application/json],custom=[]}"
onto public xxx.endpoint.ThemeEndpoint$Asset
xxx.endpoint.ThemeEndpoint.getCssItem(
net.lacho.svc.themes.api.ThemeId,java.lang.String)
and with a non .css extension the controller is called:
Found 1 matching mapping(s) for [/themes/ac18a080-a2f1-11e3-84f4-600308a0bd14/css/main.less]
: [{[/themes/{id}/css/{assetName:.*}],methods=[GET],params=[],headers=[],
consumes=[],produces=[application/json],custom=[]}]
but as soon as i use .css as extension - bang:
Looking up handler method for path /themes/ac18a080-a2f1-11e3-84f4-600308a0bd14/css/test.css
org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver -
Resolving exception from handler [null]:
org.springframework.web.HttpMediaTypeNotAcceptableException:
Could not find acceptable representation
web.xml
and MVC-Config as requested:
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0"
metadata-complete="false">
<welcome-file-list>
<welcome-file>index</welcome-file>
</welcome-file-list>
</web-app>
WebApplicationInitializer:
package net.lacho.opcenter.bootstrap;
public class WebApplicationBootstrapper implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.setConfigLocation(ApplicationConfig.class.getName());
container.addListener(new ContextLoaderListener(rootContext));
registerRestDispatcher(container);
registerDefaultDispatcher(container);
container.addFilter("CharacterEncodingFilter", UTF8EncodingFilter.class).addMappingForUrlPatterns(null, true, "/*");
container.addFilter("headSupportFilter", HeadSupportFilter.class).addMappingForUrlPatterns(null, true, "/*");
DelegatingFilterProxy shallowFrontendContextFilterProxy = new DelegatingFilterProxy("shallowFrontendContextProviderLocalFilter");
shallowFrontendContextFilterProxy.setTargetFilterLifecycle(true);
FilterRegistration.Dynamic shallowFrontendFilter = container.addFilter("ShallowFrontendContextFilter", shallowFrontendContextFilterProxy);
shallowFrontendFilter.setInitParameter("ignoreNullClient", "true");
shallowFrontendFilter.addMappingForUrlPatterns(null, true, "/*");
container.addFilter("springSecurityFilterChain", new DelegatingFilterProxy("springSecurityFilterChain"))
.addMappingForUrlPatterns(null, true, "/*");
container.addFilter("FrontendContextFilter", new DelegatingFilterProxy("frontendContextProviderLocalFilter"))
.addMappingForUrlPatterns(null, true, "/*");
container.addFilter("hiddenHttpMethodFilter", new HiddenHttpMethodFilter()).addMappingForUrlPatterns(null, true, "/rest/*");;
}
public void registerRestDispatcher(ServletContext container) {
AnnotationConfigWebApplicationContext restDispatcherContext = new AnnotationConfigWebApplicationContext();
restDispatcherContext.register(RestCommonsMvcConfig.class);
ServletRegistration.Dynamic restDispatcher = container.addServlet("rest-dispatcher", new DispatcherServlet(restDispatcherContext));
restDispatcher.setLoadOnStartup(1);
restDispatcher.addMapping("/rest/*");
}
public void registerDefaultDispatcher(ServletContext container) {
AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
dispatcherContext.register(MvcConfig.class);
ServletRegistration.Dynamic dispatcher = container.addServlet("backend-dispatcher", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/*", "/index");
}
}
MVC-config:
package net.lacho.opcenter.bootstrap;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"net.lacho.opcenter.ui"} )
public class MvcConfig extends WebMvcConfigurerAdapter {
... many lines removed, containing interceptors and velocity-config ...
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/_r/_s/**")
.addResourceLocations("classpath:/static-resources/")
.setCachePeriod(365 * 86400);
registry
.addResourceHandler("/_r/_d/**")
.addResourceLocations("classpath:/static-uncached-resources/");
}
@Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
Any idea anyone?
I had required an extra step in my special case. I would like to add an extra information to @dirk-lachowski's great comment in case someone else needs. In my case I am importing an extension from
WebMvcConfigurationSupport
classIf you need to override
requestMappingHandlerMapping
method then you also have to addm.setUseSuffixPatternMatch(false);
It's not a bug, it's a feature...
As @axtavt and @rhinds have supposed, something is messing around which the content type. The browsers sends a correct
Accept: application/json
but spring ignores this and uses the extension of the url (aarrgh). From the docs:The solution is quite simple as you can disable this "feature":
See also Spring does not ignore file extension for xml-config.
Related