EnableWebMvc throws ServletException: Could not resolve view with name - java

Playing around with Spring Boot + MVC with static HTML pages, while noticed this thing:
Firstly, what I have:
Index controller:
#Controller
public class IndexController {
#RequestMapping("/")
public String index() {
return "index.html";
}
#RequestMapping("/{path:[^\\.]+}/**")
public String forward() {
return "forward:/";
}
}
The Html file is:...\src\main\resources\static\index.html
So when my main application class is:
#SpringBootApplication
public class MyApplication extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Everything works well and in default path: localhost:8080\ I get index.html page content
But if I annotate Application class with #EnableWebMvc
#SpringBootApplication
#EnableWebMvc
public class MyApplication extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
I get exception: javax.servlet.ServletException: Could not resolve view with name 'index.html' in servlet with name 'dispatcherServlet'
But according to this spring doc it is a valid configuration.
Maybe someone can explain me why? Do I understand something wrong?

According to spring-boot's docs
The auto-configuration adds the following features on top of Spring’s defaults:
Static index.html support.
...
If you want to keep Spring Boot MVC features, and you just want to add
additional MVC configuration (interceptors, formatters, view
controllers etc.) you can add your own #Configuration class of type
WebMvcConfigurerAdapter, but without #EnableWebMvc. If you wish to
provide custom instances of RequestMappingHandlerMapping,
RequestMappingHandlerAdapter or ExceptionHandlerExceptionResolver you
can declare a WebMvcRegistrationsAdapter instance providing such
components.
So by adding #EnableWebMvc you just disable what spring-boot autoconfiguring for you. Namely static index.html support.

Actually I think when you choose to use spring boot you should use the default config of spring Boot. It means you just have to edit the file application.properties. Now if you use spring mvc, you have to provide your own servlet. So I think mixing up the to is not a good idea. Either you use spring Boot wiht no much config to do or you use spring mvc and you make all the necessary config.

According to Spring Boot MVC structure, you should locate your html file in the templates folder. Then will be visible for Spring Boot
src\main\resources\templates\index.html

Related

Is it possible to exclude nested configuration from autoconfiguration in Spring Boot application?

Suppose we have a Spring Boot application and autoconfiguration with several configurations defined inside it
#Configuration
#AutoConfigureBefore(MainAutoConfiguration.class)
public class TestAutoConfiguration {
....
#Configuration
public static class FirstNestedConfiguration {
...
}
#Configuration
public static class SecondNestedConfiguration {
...
}
}
this class is providing via external library dependency and all conditions are satisfied, so all beans in these configurations are loading.
Nevertheless, I need to exclude beans provided in FirstNestedConfiguration
Is it possible to do it?
UPD: as it's simple Spring Boot application, it runs as
#SpringCloudApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
without any explicit #ComponentScan configuration
You might use excludeFilters in #ComponentScan like this:
#ComponentScan(value = {'your.package'},
excludeFilters = #Filter(TestAutoConfiguration.class))
Also, if you want to exclude specific autoconfiguration globally, use properties:
spring:
autoconfigure.exclude: your.package.TestAutoConfiguration
However, please, note that this way you exclude outer configuration. According to this issue it's not possible to exclude inner configuration.
Does the profile approach works for you?
Look for With the #Profile annotation section
#Profile("ConfigOne")
#Configuration
Configuration spring documentation

Webjars + Springboot. Custom resource path

I have a Spring Boot application which acts as a server for my frontend, built by webpack and included into my spring boot web archive.
I use webjars to access my frontend scripts and contents.
But there is one problem. To access webjars resources I need to use pathes like:
/webjars/jar-file-name/resource-name.ext
When in my react-js frontend code I use relateve pathes:
/resource-name.ext
I want to rebind paths of webjars to serve all resources /** from /webjars/jar-file-name
I have used this do to do it https://www.webjars.org/documentation#springmvc, but this seems to not work with Spring Boot
#Configuration
#EnableWebMvc
public class MvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/webjars/jar-file-name/");
}
}
It should work with Spring MVC, but don't work in Spring Boot.
Could you please advice the right way to do it?
I think the following code would resolve your issue:
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
Here is my static content serving URL for bootstrap 3.1.0 from tutorial: http://localhost:8080/bootstrap/3.1.0/css/bootstrap.min.css

Spring Boot map URL request to a Static Resource, not serving index.html by default

I have a spring boot app serving angular resources in my static [resources/static] folder. I also am using the same project to serve my JSON-REST-API endpoints.
Hence I have defined my REST api under localhost:9090/api/...
My Angular2 app-build is served under localhost:9090/ui/... via static resources
Now I want to forward all my ui/** urls to ui/index.html/**
How do I do this?
P.S. I have introduced a custom static resource url pattern
spring.mvc.static-path-pattern=/ui/**
Then all my ui/** request will look to the static/**
This way I was able to secure my /api/** and "permitAll" ui/** requests
This simple configuration will do the trick and will even refresh your # routes in angular 2 if enabled.
#Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/ui").setViewName("forward:/ui/index.html");
registry.addViewController("/ui/").setViewName("forward:/ui/index.html");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
}
Pointers:
- #SpringBootApplication should be annotated where the spring boot app is run
- Do not have #EnableWebMvc as this breaks all auto configuration done by spring boot
- See whether this configuration is under a directory you have marked for
#ComponentScan({"com.foo.config"})
- This implementation is specifically tailored for situations where you have a custom spring.mvc.static-path-pattern defined [ui/]

Spring Boot Actuator for custom Servlet

I noticed Spring Boot Actuator only works if your application uses Spring MVC (DispatcherServlet) to handle endpoints. By default, this servlet is included if you add the module spring-boot-starter-web into your project.
Once this servlet exists, the class EndpointWebMvcAutoConfiguration customize the Spring MVC to support endpoints and other management properties.
For record, my application implements a Vaadin Servlet to navigate on screens, so is there any way to enable Spring Boot Actuator in this case?
You won't be able to reuse the EndpointWebMVCAutoConfiguration class as it is explicitly conditionnal on DispatcherServlet.class. If you look at the implementation, you'll see that the Actuator has a lot of dependencies on Spring MVC.
It would be a little ballzy but you could consider implementing your own autoconfiguration class inspired by EndpointWebMVCAutoConfiguration.
I wish you good luck if you go down that path ;)
You can have both. If you have a VaadinServlet, you can try with something like:
#SpringBootApplication
public class AdminApplication {
#Bean
public ServletRegistrationBean<SpringVaadinServlet> springVaadinServlet() {
SpringVaadinServlet servlet = new SpringVaadinServlet();
ServletRegistrationBean<SpringVaadinServlet> registrationBean = new ServletRegistrationBean<>(servlet, "/your-web-app/*");
registrationBean.setLoadOnStartup(1);
registrationBean.setName("VaadinServlet");
return registrationBean;
}
}
#SpringUI(path = "/")
public class VaadinUI extends UI {
...
}
Notice the need for a registration name, a custom servlet mapping URL, and custom path in the #SpringUI annotation.
You can find a running demo here.

How to set base url for rest in spring boot?

I'm trying to to mix mvc and rest in a single spring boot project.
I want to set base path for all rest controllers (eg. example.com/api)
in a single place (I don't want annotate each controller with #RequestMapping('api/products'), instead, just #RequestMapping('/products').
Mvc controllers should be accessible by example.com/whatever
Is it possible?
(I don't use spring data rest, just spring mvc)
With Spring Boot 1.2+ (<2.0) all it takes is a single property in application.properties:
spring.data.rest.basePath=/api
ref link : https://docs.spring.io/spring-data/rest/docs/current/reference/html/#getting-started.changing-base-uri
For 2.x, use
server.servlet.context-path=/api
A bit late but the same question brought me here before reaching the answer so I post it here.
Create (if you still don't have it) an application.properties and add
server.contextPath=/api
So in the previous example if you have a RestController with #RequestMapping("/test") you will access it like localhost:8080/api/test/{your_rest_method}
question source: how do i choose the url for my spring boot webapp
For spring boot framework version 2.0.4.RELEASE+. Add this line to application.properties
server.servlet.context-path=/api
Try using a PathMatchConfigurer (Spring Boot 2.x):
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("api", HandlerTypePredicate.forAnnotation(RestController.class));
}
}
I couldn't believe how complicate the answer to this seemingly simple question is. Here are some references:
Spring JIRA Ticket
Another SO question
Yet another SO question
Very nice GitRepository that showcases the problem
There are many differnt things to consider:
By settingserver.context-path=/api in application.properties you can configure a prefix for everything.(Its server.context-path not server.contextPath !)
Spring Data controllers annotated with #RepositoryRestController that expose a repository as rest endpoint will use the environment variable spring.data.rest.base-path in application.properties. But plain #RestController won't take this into account. According to the spring data rest documentation there is an annotation #BasePathAwareController that you can use for that. But I do have problems in connection with Spring-security when I try to secure such a controller. It is not found anymore.
Another workaround is a simple trick. You cannot prefix a static String in an annotation, but you can use expressions like this:
#RestController
public class PingController {
/**
* Simple is alive test
* #return <pre>{"Hello":"World"}</pre>
*/
#RequestMapping("${spring.data.rest.base-path}/_ping")
public String isAlive() {
return "{\"Hello\":\"World\"}";
}
}
Since this is the first google hit for the problem and I assume more people will search for this. There is a new option since Spring Boot '1.4.0'.
It is now possible to define a custom RequestMappingHandlerMapping that allows to define a different path for classes annotated with #RestController
A different version with custom annotations that combines #RestController with #RequestMapping can be found at this blog post
#Configuration
public class WebConfig {
#Bean
public WebMvcRegistrationsAdapter webMvcRegistrationsHandlerMapping() {
return new WebMvcRegistrationsAdapter() {
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping() {
private final static String API_BASE_PATH = "api";
#Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
Class<?> beanType = method.getDeclaringClass();
if (AnnotationUtils.findAnnotation(beanType, RestController.class) != null) {
PatternsRequestCondition apiPattern = new PatternsRequestCondition(API_BASE_PATH)
.combine(mapping.getPatternsCondition());
mapping = new RequestMappingInfo(mapping.getName(), apiPattern,
mapping.getMethodsCondition(), mapping.getParamsCondition(),
mapping.getHeadersCondition(), mapping.getConsumesCondition(),
mapping.getProducesCondition(), mapping.getCustomCondition());
}
super.registerHandlerMethod(handler, method, mapping);
}
};
}
};
}
}
You can create a custom annotation for your controllers:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#RestController
#RequestMapping("/test")
public #interface MyRestController {
}
Use it instead of the usual #RestController on your controller classes and annotate methods with #RequestMapping.
Just tested - works in Spring 4.2!
For Boot 2.0.0+ this works for me: server.servlet.context-path = /api
I found a clean solution, which affects only rest controllers.
#SpringBootApplication
public class WebApp extends SpringBootServletInitializer {
#Autowired
private ApplicationContext context;
#Bean
public ServletRegistrationBean restApi() {
XmlWebApplicationContext applicationContext = new XmlWebApplicationContext();
applicationContext.setParent(context);
applicationContext.setConfigLocation("classpath:/META-INF/rest.xml");
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setApplicationContext(applicationContext);
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet, "/rest/*");
servletRegistrationBean.setName("restApi");
return servletRegistrationBean;
}
static public void main(String[] args) throws Exception {
SpringApplication.run(WebApp.class,args);
}
}
Spring boot will register two dispatcher servlets - default dispatcherServlet for controllers, and restApi dispatcher for #RestControllers defined in rest.xml:
2016-06-07 09:06:16.205 INFO 17270 --- [ main] o.s.b.c.e.ServletRegistrationBean : Mapping servlet: 'restApi' to [/rest/*]
2016-06-07 09:06:16.206 INFO 17270 --- [ main] o.s.b.c.e.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
The example rest.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<context:component-scan base-package="org.example.web.rest"/>
<mvc:annotation-driven/>
<!-- Configure to plugin JSON as request and response in method handler -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonMessageConverter"/>
</list>
</property>
</bean>
<!-- Configure bean to convert JSON to POJO and vice versa -->
<bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</bean>
</beans>
But, you're not limited to:
use XmlWebApplicationContext, you may use any else context type available, ie. AnnotationConfigWebApplicationContext, GenericWebApplicationContext, GroovyWebApplicationContext, ...
define jsonMessageConverter, messageConverters beans in rest context, they may be defined in parent context
I did some research on the differences of spring properties mentioned in this thread. Here are my findings if anybody is wondering.
spring.data.rest.basePath Property
spring.data.rest.basePath=/api
This property is specifically for Spring Data Rest projects. It won't work in a usual Spring MVC projects.
To change the context path in MVC projects, you can use those two properties mentioned below. Let me mention the differences too.
server.servlet.context-path Property
server.servlet.context-path=/api
This one sets the context path on your web servelet. This property perfectly works fine in both spring mvc and spring data rest projects. But, the differnce is the request url will be filter out before reaching spring interceptors. So it will respond with HTML on bad request. Not Spring's or your own custom JSON response (in #ResponseBodyAdvice annotated class) defined. To overcome that, you should use this property below.
spring.mvc.servlet.path Property
spring.mvc.servlet.path=/api
This will filter the request URL in spring mvc interceptors and will respond default/your custom JSON response if you invoke a bad request.
Conclusion:
So as the OP's question, I would suggest that he should use spring.mvc.servlet.path to change the context path.
I might be a bit late, BUT... I believe it is the best solution. Set it up in your application.yml (or similar config file):
spring:
data:
rest:
basePath: /api
As I can remember that's it - all of your repositories will be exposed beneath this URI.
You can create a base class with #RequestMapping("rest") annotations and extend all you other classes with this base class.
#RequestMapping("rest")
public abstract class BaseController {}
Now all classes that extend this base class will be accessible at rest/**.
With spring-boot 2.x you can configure in application.properties:
spring.mvc.servlet.path=/api
For those who use YAML configuration(application.yaml).
Note: this works only for Spring Boot 2.x.x
server:
servlet:
contextPath: /api
If you are still using Spring Boot 1.x
server:
contextPath: /api
server.servlet.context-path=/api would be the solution I guess. I had the same issue and this got me solved. I used server.context-path. However, that seemed to be deprecated and I found that server.servlet.context-path solves the issue now. Another workaround I found was adding a base tag to my front end (H5) pages. I hope this helps someone out there.
Cheers
You can create a custom annotation for your controllers:
Use it instead of the usual #RestController on your controller classes and annotate methods with #RequestMapping.
Works fine in Spring 4.2!
For Spring WebFlux the approach is similar to Harald's, but with the obvious WebFlux configuration set up:
#Configuration
public class WebFluxConfig implements WebFluxConfigurer {
#Override
public void configurePathMatching(PathMatchConfigurer configurer) {
configurer.addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController.class));
}
}
And for Kotlin it's:
#Configuration
class WebFluxConfig : WebFluxConfigurer {
override fun configurePathMatching(configurer: PathMatchConfigurer) {
configurer.addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController::class.java))
}
This solution applies if:
You want to prefix RestController but not Controller.
You are not using Spring Data Rest.
#Configuration
public class WebConfig extends WebMvcConfigurationSupport {
#Override
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new ApiAwareRequestMappingHandlerMapping();
}
private static class ApiAwareRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
private static final String API_PATH_PREFIX = "api";
#Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
Class<?> beanType = method.getDeclaringClass();
if (AnnotationUtils.findAnnotation(beanType, RestController.class) != null) {
PatternsRequestCondition apiPattern = new PatternsRequestCondition(API_PATH_PREFIX)
.combine(mapping.getPatternsCondition());
mapping = new RequestMappingInfo(mapping.getName(), apiPattern, mapping.getMethodsCondition(),
mapping.getParamsCondition(), mapping.getHeadersCondition(), mapping.getConsumesCondition(),
mapping.getProducesCondition(), mapping.getCustomCondition());
}
super.registerHandlerMethod(handler, method, mapping);
}
}
}
This is similar to the solution posted by mh-dev, but I think this is a little cleaner and this should be supported on any version of Spring Boot 1.4.0+, including 2.0.0+.
Per Spring Data REST docs, if using application.properties, use this property to set your base path:
spring.data.rest.basePath=/api
But note that Spring uses relaxed binding, so this variation can be used:
spring.data.rest.base-path=/api
... or this one if you prefer:
spring.data.rest.base_path=/api
If using application.yml, you would use colons for key separators:
spring:
data:
rest:
basePath: /api
(For reference, a related ticket was created in March 2018 to clarify the docs.)
worked server.contextPath=/path

Categories