I am using the following components in my REST back-end project:
I'm using XML based bean configuration and I was able to get the basic setup with Swagger up and running, using the following in Spring XML Configuration file:
<mvc:annotation-driven/> <!-- Required so swagger-springmvc can access spring's RequestMappingHandlerMapping -->
<bean class="com.mangofactory.swagger.configuration.SpringSwaggerConfig" />
I then cloned the Swagger-UI GIT repo from https://github.com/wordnik/swagger-ui and copied of the dist folder to my /webapp/docs folder. After doing so I was able to use the JS based UI on the following URL:
http://localhost:9090/docs/index.html (works so far).
The problem is that none of the try it out buttons worked, which can be used to interact with your REST API using JSON directly. The reason that it did not work is that it did not take the correct base url path to talk to my controllers:
http://localhost:9090/rest/addresses (actual controller location)
http://localhost:9090/addresses (swagger's attempt to talk to the API)
After some research online I found out that I probably better go for the more flexible approach using SwaggerSpringMvcPlugin as documented on https://github.com/martypitt/swagger-springmvc.
This is my swagger configuration class file:
@Configuration
@EnableSwagger
public class MySwaggerConfig {
private SpringSwaggerConfig springSwaggerConfig;
@Autowired
public void setSpringSwaggerConfig(SpringSwaggerConfig springSwaggerConfig) {
this.springSwaggerConfig = springSwaggerConfig;
}
@Bean
public SwaggerSpringMvcPlugin customImplementation(){
return new SwaggerSpringMvcPlugin(this.springSwaggerConfig)
.includePatterns(".*");
}
}
This is my full spring mvc xml file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="be.exampley.backend.web.rest"/>
<bean id="hibernateAwareObjectMapper" class="be.example.backend.util.HibernateAwareObjectMapper"/>
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"
p:prettyPrint="true"
p:supportedMediaTypes-ref="supportedMediaTypes"
p:objectMapper-ref="hibernateAwareObjectMapper"/>
</mvc:message-converters>
</mvc:annotation-driven>
<util:list id="supportedMediaTypes">
<value>application/json</value>
<value>text/plain</value>
</util:list>
<context:annotation-config/>
<mvc:default-servlet-handler/>
<context:property-placeholder location="classpath:swagger.properties"/>
<bean name="swaggerConfig" class="be.example.backend.configuration.MySwaggerConfig"/>
</beans>
An extract of the relevant section from my web.xml file:
<servlet>
<servlet-name>restServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/spring/mvc-rest-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>restServlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
This is my swagger.properties file, which is not taken into account at all for some reason.
documentation.services.basePath=/rest/
documentation.services.version=2.0
The stacktrace I get during application startup:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mySwaggerConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void be.example.backend.configuration.MySwaggerConfig.setSpringSwaggerConfig(com.mangofactory.swagger.configuration.SpringSwaggerConfig); nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.mangofactory.swagger.configuration.SpringSwaggerConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.util.List com.mangofactory.swagger.configuration.SpringSwaggerConfig.handlerMappings; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] found for dependency [collection of org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1204)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:725)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
at org.eclipse.jetty.server.handler.ContextHandler.callContextInitialized(ContextHandler.java:798)
at org.eclipse.jetty.servlet.ServletContextHandler.callContextInitialized(ServletContextHandler.java:444)
at org.eclipse.jetty.server.handler.ContextHandler.startContext(ContextHandler.java:789)
at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:294)
at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1341)
at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1334)
at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:741)
at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:497)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:163)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
at org.eclipse.jetty.server.Server.start(Server.java:387)
at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
at org.eclipse.jetty.server.Server.doStart(Server.java:354)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
at org.eclipse.jetty.runner.Runner.run(Runner.java:509)
at org.eclipse.jetty.runner.Runner.main(Runner.java:557)
...
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] found for dependency [collection of org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1261)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:961)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:904)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:527)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1204)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1081)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1006)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:904)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:600)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1204)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:725)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
at org.eclipse.jetty.server.handler.ContextHandler.callContextInitialized(ContextHandler.java:798)
at org.eclipse.jetty.servlet.ServletContextHandler.callContextInitialized(ServletContextHandler.java:444)
at org.eclipse.jetty.server.handler.ContextHandler.startContext(ContextHandler.java:789)
at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:294)
at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1341)
at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1334)
at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:741)
at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:497)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:163)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
at org.eclipse.jetty.server.Server.start(Server.java:387)
at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
at org.eclipse.jetty.server.Server.doStart(Server.java:354)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
at org.eclipse.jetty.runner.Runner.run(Runner.java:509)
at org.eclipse.jetty.runner.Runner.main(Runner.java:557)
I read somewhere that the No qualifying bean of type org...RequestMappingHandlerMapping trace suggests I might miss mvc:annotation-driven in my XML, but this is not the case.
I have been searching for hours now to find a solution, but failed so far. The fact that in it's basic configuration (using SpringSwaggerConfig) it's working for 90% (besides the try it out button...), makes me not want to give up on swagger just yet. It really seems very cool and is a good way of documenting your API.
So if anyone can help me out on this one, I would be very gratefull. Swagger rocks!
Kind regards, Bart
I was probably stumbling upon the following issue, "No matching bean was found for auto-wiring" for RequestMappingHandlerMapping: http://forum.spring.io/forum/spring-projects/web/112154-unable-to-autowire-requestmappinghandlermapping-in-controller
In the mean while I have switched to Java based configuration to replace my XML. This solved the autowire issue as well. Hereby my solution for other's reference, which includes changing the apiResourcePrefix and ussing a swagger.properties file for external configuration.
My web.xml file:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/business.xml</param-value>
</context-param>
<servlet>
<servlet-name>restServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>be.example.backend.configuration.MvcConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>restServlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
My bussiness.xml as root application context:
<context:annotation-config/>
<context:component-scan base-package="be.example.backend"/>
...
My MvcConfig.java as MVC configuration file:
@EnableWebMvc
@ComponentScan(basePackages = {"be.example.backend"})
@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
List<MediaType> supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
supportedMediaTypes.add(MediaType.TEXT_PLAIN);
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(new HibernateAwareObjectMapper());
converter.setPrettyPrint(true);
converter.setSupportedMediaTypes(supportedMediaTypes);
converters.add(converter);
super.configureMessageConverters(converters);
}
}
My SwaggerConfig.java as swagger configuration file:
@EnableWebMvc
@EnableSwagger
@PropertySource("classpath:swagger.properties")
@ComponentScan(basePackages = {"be.example.backend"})
@Configuration
public class SwaggerConfig implements ServletContextAware {
@Autowired
private Environment environment;
private SpringSwaggerConfig springSwaggerConfig;
private ServletContext servletContext;
@Autowired
public void setSpringSwaggerConfig(SpringSwaggerConfig springSwaggerConfig) {
this.springSwaggerConfig = springSwaggerConfig;
}
@Bean
public SwaggerSpringMvcPlugin customImplementation() {
RelativeSwaggerPathProvider relativeSwaggerPathProvider = new RelativeSwaggerPathProvider(servletContext);
relativeSwaggerPathProvider.setApiResourcePrefix(environment.getProperty("swagger.resource_prefix"));
return new SwaggerSpringMvcPlugin(this.springSwaggerConfig)
.pathProvider(relativeSwaggerPathProvider);
}
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
}
My swagger.properties external properties file:
swagger.resource_prefix=rest
This solution worked for me and I am now able to enjoy the swagger features through swagger-ui.
Hope others can enjoy it as well!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With