2014-11-13 5 views
6

Ich verwende die folgenden Komponenten in meinem REST Back-End-Projekt:SwaggerSpringMvcPlugin nicht arbeiten Spring MVC

  • Spring MVC: 4.1.2.RELEASE (latest)
  • Hibernate4 Object Mapper: 2.4.3 (spätestens)
  • Swagger: 0.9.1 (spätestens)
  • JavaEE-web-api: 7.0 (Servlet 3,1)

ich verwende XML-basierte Bean-Konfiguration und ich war in der Lage zu bekommen die Grundeinstellung mit Swagger und läuft, den folgend in Spring-XML-Konfigurationsdatei:

<mvc:annotation-driven/> <!-- Required so swagger-springmvc can access spring's RequestMappingHandlerMapping --> 

<bean class="com.mangofactory.swagger.configuration.SpringSwaggerConfig" /> 

ich dann von https://github.com/wordnik/swagger-ui kloniert und die dist-Ordner auf meinen /webapp/docs die Swagger-UI GIT-Repo kopiert Mappe. Nachdem ich so konnte ich die JS basierte Benutzeroberfläche auf die folgende URL verwenden: es

http://localhost:9090/docs/index.html (works so far). 

Das Problem ist, dass keiner der ausprobieren Tasten gearbeitet, die mit Ihrem REST-API verwendet werden kann, zu interagieren mit JSON direkt. Der Grund, dass es nicht ist arbeiten, dass es nicht den richtigen Basis-URL-Pfad nehmen hat meinen Controller zu sprechen:

http://localhost:9090/rest/addresses (actual controller location) 
http://localhost:9090/addresses (swagger's attempt to talk to the API) 

Nach einiger Online-Forschung fand ich heraus, dass ich wahrscheinlich gehe besser für den flexibleren Ansatz mit SwaggerSpringMvcPlugin wie auf https://github.com/martypitt/swagger-springmvc dokumentiert.

Dies ist meine Prahlerei Konfigurationsklasse Datei:

@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(".*"); 
    } 
} 

Das ist mein voller Spring MVC xml Datei:

<?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> 

Ein Auszug aus dem entsprechenden Abschnitt von meinem Web. xml Datei:

<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> 

Das ist meine swagger.properties Datei, die aus irgendeinem Grund überhaupt nicht berücksichtigt wird.

documentation.services.basePath=/rest/ 
documentation.services.version=2.0 

Die Stacktrace ich beim Anwendungsstart erhalten:

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) 

ich irgendwo gelesen, dass die Keine Warte Bohnen vom Typ org ... RequestMappingHandlerMapping Spur legt nahe, ich mvc verpassen könnte: Annotation-driven in meinem XML, aber das ist nicht der Fall.

Ich habe seit Stunden gesucht, um eine Lösung zu finden, aber bisher gescheitert. Die Tatsache, dass es in seiner Grundkonfiguration (mit SpringSwaggerConfig) funktioniert es für 90% (neben der probieren Sie es aus Knopf ...), will ich nicht schon jetzt aufgeben. Es scheint wirklich sehr cool und ist eine gute Möglichkeit, Ihre API zu dokumentieren.

Also wenn mir jemand hier helfen kann, wäre ich sehr dankbar. Swagger rockt!

Mit freundlichen Grüßen, Bart

Antwort

6

ich wahrscheinlich auf das folgende Problem stolperte, für RequestMappingHandlerMapping „keine passende Bohne wurde für die automatische Verdrahtung gefunden“: http://forum.spring.io/forum/spring-projects/web/112154-unable-to-autowire-requestmappinghandlermapping-in-controller

In der Zwischenzeit, während ich auf eingeschaltet haben Java-basierte Konfiguration, um meine XML zu ersetzen. Dies löste auch das Problem mit dem Autowire. Hiermit meine Lösung für andere Referenz, einschließlich der Änderung der apiResourcePrefix und ussing swagger.properties Datei für die externe Konfiguration.

Meine web.xml Datei:

<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> 

Mein bussiness.xml als Kontext-Root-Anwendung:

<context:annotation-config/> 
<context:component-scan base-package="be.example.backend"/> 
... 

Mein MvcConfig.java als MVC-Konfigurationsdatei:

@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); 
    } 
} 

Mein SwaggerConfig.java als Prahlerei Konfigurationsdatei:

@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; 
    } 
} 

Mein swagger.properties externe Eigenschaftsdatei:

swagger.resource_prefix=rest 

Diese Lösung funktionierte für mich und ich bin nun in der Lage das genießen Swagger Features durch Swagger-Ui.

Hoffe andere können es auch genießen!

1

Ich hatte genau gleiche Ausnahme

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) 

in meinem Test. Root Casue war falsche Annotation. Mein Test wurde mit @Integration annotiert, anstatt mit @WebIntegrationTest.

Für die Produktion Code, stellen Sie sicher, dass Ihre @Configuration ist kommentierten mit @EnableWebMvc