Spring serves static content as application/json no matter what - spring

I have a simple Spring Boot app with a static content. I would like the browsers to cache not all but just some of the types, like css. So I added following configuration:
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/css/**").addResourceLocations("/css/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS));
}
But now all the CSS files are server as Content-Type: application/json. Without this code they are served as text/css. I cannot figure out what am I doing wrong. Is there anything I should check or add?
Thanks in advance.

you have to mention extension of the file name like *.css want to cache
#Configuration
public class BaseMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/css/*.css").addResourceLocations("classpath:/static/").setCachePeriod(24 * 3600 * 365);
registry.addResourceHandler("/static/js/*.js").addResourceLocations("classpath:/static/").setCachePeriod(0);
}
}

I have resolved this by adding #EnableWebMvc annotation and applying this configuration by:
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/css/*.css")
.addResourceLocations("classpath:/static/css/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
.resourceChain(true).addResolver(new PathResourceResolver());
registry.addResourceHandler("/js/**")
.addResourceLocations("classpath:/static/js/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
.resourceChain(true).addResolver(new PathResourceResolver());
registry.addResourceHandler("/img/**")
.addResourceLocations("classpath:/static/img/")
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
.resourceChain(true).addResolver(new PathResourceResolver());
registry.addResourceHandler("/*.html")
.addResourceLocations("classpath:/static/").setCachePeriod(0);
registry.addResourceHandler("/*.png")
.addResourceLocations("classpath:/static/").setCachePeriod(0);
}
#Bean
public InternalResourceViewResolver defaultViewResolver() {
return new InternalResourceViewResolver();
}
The resolver is important in this case.
I assume that I made some mistakes when using Spring Boot features and the #EnableWebMvc helped a lot.

Related

Spring boot: Log file has get requests to static files

I have configured my app using
#Configuration
#EnableWebSecurity
#Slf4j
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {...}
Inside configure method, allowed the static content.
http .httpBasic().and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/**/*.js", "/resources/static/**", "/static/**", "/**/*.jpg", "/**/*.svg", "/**/*.ico", "/**/*.gif", "/**/*.css", "/register")
.permitAll()
application.properties :
logging.level.web=debug
logging.level.org.springframework.web=debug
The problem i am having is that my log file is crowded by get requests to the static resources
I am just interested in logging access to controllers and not static files.
Is there something i need to configure to exclude the logging of access to static resources?
I have also tried
#Configuration
public class StaticConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!registry.hasMappingForPattern("/static/**")) {
registry.addResourceHandler("/static/**")
.addResourceLocations("/static/");
}
}
}
Issue
You have enabled debug level logging for org.springframework.web package, so it will log everything
Try this solution
Remove the debug level logging for org.springframework.web
Create a subclass of CommonsRequestLoggingFilter
public class CustomRequestLoggingFilter extends CommonsRequestLoggingFilter {
#Override
protected boolean shouldLog(HttpServletRequest request) {
return logger.isDebugEnabled() &&
!request.getRequestURL().toString().contains("/static")
}
}
Create that filter as a bean
#Configuration
public class RequestLoggingFilterConfig {
#Bean
public CustomRequestLoggingFilter logFilter() {
CustomRequestLoggingFilter filter
= new CustomRequestLoggingFilter();
filter.setIncludeQueryString(true);
filter.setIncludePayload(false);
filter.setMaxPayloadLength(10000);
filter.setIncludeHeaders(false);
filter.setAfterMessagePrefix("REQUEST DATA : ");
return filter;
}
}
Add the following to application.properties
logging.level.<package of your filter>.CustomRequestLoggingFilter=DEBUG

Spring Boot + JSF static page configurations

am getting started with Spring Boot + JSF.
Spring documentation says:
Spring will automatically load and serve resources inside
/static (or /public or /resources or /META-INF/resources.
This is not working for me.
it is discouraged to use the "webapp" (src/main/webapp) folder.
But for my static content, it seem to be the only folder thats working with my JSF static page.
see Spring Boot static content.
I have tried these configuration:
#Configuration
public class JSFServletConfigs implements ServletContextInitializer// ,ServletContextAware
{
#Override // ServletContext initiallization parameters
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setInitParameter("facelets.DEVELOPMENT", "false");
servletContext.setInitParameter("javax.faces.PROJECT_STAGE", "Development");
servletContext.setInitParameter("javax.faces.DEFAULT_SUFFIX", ".xhtml");
servletContext.setInitParameter("javax.faces.PARTIAL_STATE_SAVING_METHOD", "true");
servletContext.setInitParameter("javax.faces.FACELETS_REFRESH_PERIOD", "-1");
}
#Bean // Registering the FacesServlet.
public ServletRegistrationBean servletRegistrationBean() {
FacesServlet servlet = new FacesServlet();
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(servlet) {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
FacesInitializer facesInitializer = new FacesInitializer();
Set<Class<?>> clazz = new HashSet<Class<?>>();
clazz.add(JSFServletConfigs.class);
facesInitializer.onStartup(clazz, servletContext);
}
};
servletRegistrationBean.addUrlMappings("*.xhtml", "*.jsf", "*.html");
return servletRegistrationBean;
}
#Bean
public InternalResourceViewResolver jsfViewResolving(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/<sub-folder of any LOCATION>/");
viewResolver.setSuffix(".xhtml");
return viewResolver;
}
}
Servlet pre-configs:
#Configuration
#EnableWebMvc
public class DefaultServletConfigs extends WebMvcConfigurerAdapter {
#Override // Enabling the default Servlet at path="/"
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
private final String[] LOCATIONS = {"<All my possible locations>"};
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if(!registry.hasMappingForPattern("/**")){
registry.addResourceHandler("/**").addResourceLocations(LOCATIONS);
}
if(!registry.hasMappingForPattern("webjars")){
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
}
My Question are:
What am I missing here?
How do I get the flexibility to place my resources in any of the other folders above
(i.e. "/static", "/public", "/resources", "/META-INF/resources").

Spring boot single page application - forward every request to index.html

I have a Spring Boot (v1.3.6) single page application (angular2) and i want to forward all request to the index.html.
A request to http://localhost:8080/index.html is working (200 and i get the index.html) but http://localhost:8080/home is not (404).
Runner.class
#SpringBootApplication
#ComponentScan({"packagea.packageb"})
#EnableAutoConfiguration
public class Runner {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext run = SpringApplication.run(Runner.class, args);
}
}
WebAppConfig.class
#Configuration
#EnableScheduling
#EnableAsync
public class WebAppConfig extends WebMvcConfigurationSupport {
private static final int CACHE_PERIOD_ONE_YEAR = 31536000;
private static final int CACHE_PERIOD_NO_CACHE = 0;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.setOrder(-1);
registry.addResourceHandler("/styles.css").addResourceLocations("/styles.css").setCachePeriod(CACHE_PERIOD_ONE_YEAR);
registry.addResourceHandler("/app/third-party/**").addResourceLocations("/node_modules/").setCachePeriod(CACHE_PERIOD_ONE_YEAR);
registry.addResourceHandler("/app/**").addResourceLocations("/app/").setCachePeriod(CACHE_PERIOD_NO_CACHE);
registry.addResourceHandler("/systemjs.config.js").addResourceLocations("/systemjs.config.js").setCachePeriod(CACHE_PERIOD_NO_CACHE);
registry.addResourceHandler("/**").addResourceLocations("/index.html").setCachePeriod(CACHE_PERIOD_NO_CACHE);
}
}
styles.css, /app/third-party/xyz/xyz.js,.. are working (200 and i get the correct file). Only /** to index.html is not working.
You can also add a forwarding controller like:
#Controller
public class ForwardingController {
#RequestMapping("/{path:[^\\.]+}/**")
public String forward() {
return "forward:/";
}
}
The first part {path:[^\\.]+} matches one or more of any character other than .. This makes sure request for a file.ext doesn't get handled by this RequestMapping. If you need to support sub-paths to also be forwarded, put /** outside of the {...}.
This one didn't work for me:
return "forward:/";
Thanks to Spring MVC #RestController and redirect I found a nicely working solution:
#RequestMapping(value = "/{[path:[^\\.]*}")
public void redirect(HttpServletResponse response) throws IOException {
response.sendRedirect("/");
}
Without looking at logs I'm not entirely sure why its not being mapped correctly, however if you want to map URLs to a view (HTML) then you will probably be better off using the viewController mechanism spring provides http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-config-view-controller. e.g.
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
}
}
(taken from spring docs linked above - this is how you should map a url to a view rather than re-purposing the mapping for static resources.)
I'm not sure if there is any kind of suffix filtering for the resource mapping - e.g. I don't know how spring decides to map requests to the ResourceHttpRequestHandler - have you tried (just to confirm or deny) whether something like http://localhost:8080/home.html amps to anything?
It's also possible that the html mapping you have defined above is just being ignored and the index.html is just working because of Spring-Boot's default home page behaviour: https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ResourceProperties.java#L108
I had the same problem and the following worked for me. My html files are inside src/main/resources/static/app
The key was to remove #EnableWebMvc and add "classpath:/static/app/" to addResourceLocations! Hope this helps.
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/","classpath:/static/app/", "classpath:/public/" };
#Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!registry.hasMappingForPattern("/webjars/**")) {
registry.addResourceHandler("/webjars/**").addResourceLocations(
"classpath:/META-INF/resources/webjars/");
}
if (!registry.hasMappingForPattern("/**")) {
registry.addResourceHandler("/**").addResourceLocations(
CLASSPATH_RESOURCE_LOCATIONS);
}
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
// forward requests to /admin and /user to their index.html
registry.addViewController("/portal").setViewName(
"forward:/app/index.html");
}
};
}

migrate configuration from dispatcher servlet xml to java class. Do i need both(java class and xml)?

Following is the code for my dispatcher-servlet.xml and configuration class :
Where do i put my db configuration and entity definitions?
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
super.addViewControllers(registry);
// Default view
registry.addViewController("/").setViewName("home");
registry.addViewController("/login").setViewName("login");
registry.addViewController("/home").setViewName("home");
}
#Bean
public Filter basicMetricFilter() {
return new PieRequestMetricsFilter();
}
}
You can put those configurations in MvcConfig but it's NOT a good idea. A better approach is to define one config for each architectural aspect of your application and then put related configs only in each one. Suppose you have a web application with traditional layered architecture. In this example you would have WebConfig just like your MvcConfig class, like this:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.example.web")
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
super.addViewControllers(registry);
// Default view
registry.addViewController("/").setViewName("home");
registry.addViewController("/login").setViewName("login");
registry.addViewController("/home").setViewName("home");
}
#Bean
public Filter basicMetricFilter() {
return new PieRequestMetricsFilter();
}
}
Also, you could have a RepositoryConfig that contains your data access related configurations, like this:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackges = "com.example.repository")
public class RepositoryConfig() {
// put your datasource, entity manager, jdbc templates, tx managers here
}
For wiring these configurations together, there is no need for any xml file such as dispatcher servlet or web.xml. You can define a WepApplicationInitializer that defines parent and child configs in this scenario, like this:
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { RepositoryConfig.class, SecurityConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
WebApplicationInitializer is an interface provided by Spring MVC that ensures your implementation is detected and automatically used to initialize any Servlet 3 container. An abstract base class implementation of WebApplicationInitializer named AbstractDispatcherServletInitializer makes it even easier to register the DispatcherServlet by simply overriding methods to specify the servlet mapping and the location of the DispatcherServlet configuration.
For more details please consult the spring documentation.

Spring mvc 4.0.5 long polling example

I'm trying to implement long polling in spring with DefferedResult. I'm trying to follow this example, from spring, https://github.com/rstoyanchev/spring-mvc-chat
I'm following configuration to the letter (almost, see below). When I'm launching it I got:
HTTP Status 500 - Request processing failed; nested exception is java.lang.IllegalStateException: Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "true" to servlet and filter declarations in web.xml.
Which is coming from: StandardServletAsyncWebRequest.
My config classes:
public class MVCInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses () {
return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebMvcConfig.class, PresentationConfig.class, SecurityConfig.class, EmailConfig.class };
}
#Override
protected String[] getServletMappings () {
return new String[] { "/" };
}
#Override
protected void customizeRegistration(Dynamic registration) {
registration.setAsyncSupported(true);
}
#Override
protected boolean isAsyncSupported () {
return true;
}
}
#Configuration
#EnableWebMvc
#EnableAsync
#ComponentScan (basePackages = { "com.xxx.presentation" })
public class WebMvcConfig extends WebMvcConfigurerAdapter {
private static final Log log = LogFactory.getLog(WebMvcConfig.class);
#Override
public void configureAsyncSupport (AsyncSupportConfigurer configurer) {
configurer.setDefaultTimeout(30 * 1000L);
}
public void addViewControllers (ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("teledetailer");
}
#Override
public void addResourceHandlers (ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/images/**").addResourceLocations("/images/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
}
#Bean
public ViewResolver viewResolver () {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Override
public void addReturnValueHandlers (List<HandlerMethodReturnValueHandler> returnValueHandlers) {
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
messageConverters.add(new StringHttpMessageConverter());
messageConverters.add(new MappingJackson2HttpMessageConverter());
returnValueHandlers.add(new RequestResponseBodyMethodProcessor(messageConverters));
super.addReturnValueHandlers(returnValueHandlers);
}
}
I'm using:
Java 7
Tomcat 7.0.54
Spring 4.0.5
Any help would be much appreciated. Really frustrating with lack of decent examples.
p.s. if amount of places where I tried to enable async support seems overwhelming - don't worry I'm just trying to figure out how exactly it should be done.
Thanks, and I hope you can help me.
After asking Rossen Stoyanchev for help (great guy, big thanks to him!) he pointed out that I have springSecurityFilterChain in my web.xml, so this one also needs to be configured for async support:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
</filter>
Hope it will help the rest of you who has the same problem.

Resources