How to serve single-page app in Spring Boot? - spring

I'm trying to do something very simple in Spring Boot:
Serve my api calls from /api
Serve a single-page app located in src/main/resources/static
Shouldn't be that hard, but this question has been asked a dozen times in different ways, and there doesn't seem to be an answer. It's very easy to do in Dropwizard, or when you wire up Jersey and Jetty together directly.
The best answer so far is here:
#Configuration
public class WebConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/{spring:\\w+}")
.setViewName("forward:/");
registry.addViewController("/**/{spring:\\w+}")
.setViewName("forward:/");
registry.addViewController("/{spring:\\w+}/**{spring:?!(\\.js|\\.css)$}")
.setViewName("forward:/");
}
}
The problem with this is that calls to /api/bad_missing_path get routed to the SPA, and not to an error message that says this is a bad api call. The other problem with it is that I just don't understand it. Yet another problem is it explicitly mentions .js and .css file types, and I'm going to have many more types than that. I want everything that starts with /api to be treated as an api call, and everything that doesn't start with /api to get passed through to the /static directory.
I have also tried to mount the DefaultServlet on "/*" but it didn't catch anything, either using the default Tomcat server or when I switched to Jetty.
How do I get this to work?

Related

Spring MVC Cannot configure views' path

I have a project that uses Spring Boot 2.2.5 (Spring version 5) - here is a link to a bare minimum project demonstrating my problem. In all the tutorials I followed they claim views' path can either be configured inside application.properties like this:
spring.mvc.view.prefix=/WEB-INF/jsp
spring.mvc.view.suffix=.jsp
or inside WebMvcConfigurationSupport derived class like this:
#Override
public void configureViewResolvers(final ViewResolverRegistry registry) {
registry.jsp("classpath:/", ".jsp");
}
or like this:
#Override
public void configureViewResolvers(final ViewResolverRegistry registry) {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
registry.viewResolver(resolver);
}
None of which work in my case. Spring will always serve .jsp files from src/main/webapp and from nowhere else in spite of my configuration or lack of it. No other file types will be served from that directory, not even HTML.
Some tutorials claim that when not configured Spring will serve anywhere from
src/main/resources/static
src/main/resources/public
src/main/resources/resources
src/main/java/META-INF/resources
I am yet to see this.
CSS and Javascript files will be served from src/main/resources but only if I have this in my MVC configuration:
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/");
}
Configuring this from application.properties doesn't work
spring.resources.static-locations=classpath:/
In relation to this are there other special folder names like classpath: that can be used? I tried webapp: but it doesn't seem to be expanded
UPDATE: I thought for a moment that maybe subclassing WebMvcConfigurationSupport is to blame since it acts like #EnableWebMvc. Subclassing WebMvcConfigurer brought the following error. Placing #EnableWebMvc solves it.
An attempt was made to call a method that does not exist. The attempt was made from the following location:
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration.requestMappingHandlerAdapter(WebMvcAutoConfiguration.java:369)
The following method did not exist:
'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration.requestMappingHandlerAdapter(org.springframework.web.accept.ContentNegotiationManager, org.springframework.format.support.FormattingConversionService, org.springframework.validation.Validator)'
I read somewhere that JSP view are not supported inside embedded servlets. What a nice feature! Anyway I don't thing this is related to my problem.
I would like to stick to JSP and avoid Thymeleaf as my project is based on React. I will create MVC pages in order to be Search engine friendly, though but I will figure this out along the way.
Here is a screenshot of my project's layout

Spring Boot + Jersey + view controller not working together [duplicate]

I am getting a HTTP 404 error when trying to serve index.html ( located under main/resources/static) from a spring boot app. However if I remove the Jersey based JAX-RS class from the project, then http://localhost:8080/index.html works fine.
The following is main class
#SpringBootApplication
public class BootWebApplication {
public static void main(String[] args) {
SpringApplication.run(BootWebApplication.class, args);
}
}
I am not sure if I am missing something here.
Thanks
The problem is the default setting of the Jersey servlet path, which defaults to /*. This hogs up all the requests, including request to the default servlet for static content. So the request is going to Jersey looking for the static content, and when it can't find the resource within the Jersey application, it will send out a 404.
You have a couple options around this:
Configure Jerse runtime as a filter (instead of as a servlet by default). See this post for how you can do that. Also with this option, you need to configure one of the ServletProperties to forward the 404s to the servlet container. You can use the property that configures Jersey to forward all request which results in a Jersey resource not being found, or the property that allows you to configure a regex pattern for requests to foward.
You can simply change the Jersey servlet pattern to something else other than the default. The easiest way to do that is to annotate your ResourceConfig subclass with #ApplicationPath("/root-path"). Or you can configure it in your application.properties - spring.jersey.applicationPath.

How to enable swagger in a jersey + spring-boot web application [duplicate]

I am getting a HTTP 404 error when trying to serve index.html ( located under main/resources/static) from a spring boot app. However if I remove the Jersey based JAX-RS class from the project, then http://localhost:8080/index.html works fine.
The following is main class
#SpringBootApplication
public class BootWebApplication {
public static void main(String[] args) {
SpringApplication.run(BootWebApplication.class, args);
}
}
I am not sure if I am missing something here.
Thanks
The problem is the default setting of the Jersey servlet path, which defaults to /*. This hogs up all the requests, including request to the default servlet for static content. So the request is going to Jersey looking for the static content, and when it can't find the resource within the Jersey application, it will send out a 404.
You have a couple options around this:
Configure Jerse runtime as a filter (instead of as a servlet by default). See this post for how you can do that. Also with this option, you need to configure one of the ServletProperties to forward the 404s to the servlet container. You can use the property that configures Jersey to forward all request which results in a Jersey resource not being found, or the property that allows you to configure a regex pattern for requests to foward.
You can simply change the Jersey servlet pattern to something else other than the default. The easiest way to do that is to annotate your ResourceConfig subclass with #ApplicationPath("/root-path"). Or you can configure it in your application.properties - spring.jersey.applicationPath.

How to serve an html file with .html url extension using Spring

I'm trying to use the Application Security on Cloud provided by Bluemix for a Spring application.
I'm relatively new to Spring and I'm having difficulty serving the requested verification file with the requested url. Bluemix wants the html file to be available at /IBMDomainVerification.html
However, I can't figure out how to serve the html file given that exact url using Spring. I can serve it without the .html at the end of the url but that's not what it needs.
If anyone can let me know how I can serve the html file given the specified url with the .html extension on the end that'd be great!
Thanks!
Spring 4.1 introduced the ResourceResolvers, that can resolve resources, given their URL path. In particular you could use the simplest: PathResourceResolver. This is the simplest resolver and its purpose is to find a resource given a public URL pattern. In fact, if no ResourceResolver is added to the ResourceChainRegistration, this is the default resolver.
Consider the following example:
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/","/other-resources/")
.setCachePeriod(3600)
.resourceChain(true)
.addResolver(new PathResourceResolver());
}
The above code serves anything in either the webapp/resources of the webapp/other-resources folder, required with URLs /resources/** (e.g. /resources/foo.js). Note the two asterisks, it means that it will match everything after resources/, even '/'.
In your case using /IBMDomainVerification.html (/**) it should work.
An alternative way could be using *<mvc:resources/>* (Spring 3) which can be used to serve static resources while still using the DispatchServlet on root. Please refer to this SO answer.

Spring boot not serving static content when Jersey REST is included

I am getting a HTTP 404 error when trying to serve index.html ( located under main/resources/static) from a spring boot app. However if I remove the Jersey based JAX-RS class from the project, then http://localhost:8080/index.html works fine.
The following is main class
#SpringBootApplication
public class BootWebApplication {
public static void main(String[] args) {
SpringApplication.run(BootWebApplication.class, args);
}
}
I am not sure if I am missing something here.
Thanks
The problem is the default setting of the Jersey servlet path, which defaults to /*. This hogs up all the requests, including request to the default servlet for static content. So the request is going to Jersey looking for the static content, and when it can't find the resource within the Jersey application, it will send out a 404.
You have a couple options around this:
Configure Jerse runtime as a filter (instead of as a servlet by default). See this post for how you can do that. Also with this option, you need to configure one of the ServletProperties to forward the 404s to the servlet container. You can use the property that configures Jersey to forward all request which results in a Jersey resource not being found, or the property that allows you to configure a regex pattern for requests to foward.
You can simply change the Jersey servlet pattern to something else other than the default. The easiest way to do that is to annotate your ResourceConfig subclass with #ApplicationPath("/root-path"). Or you can configure it in your application.properties - spring.jersey.applicationPath.

Resources