Добавление заглушек сервера Swagger в существующее приложение Spring

Как лучше всего подключить серверные заглушки, сгенерированные Swagger Codegen, в существующее приложение Spring MVC?

Я начинаю с попытки использовать заглушки зоомагазина образец.

Моя конфигурация Spring находится на Java и выглядит так:

public class SpringConfigurationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[] { ApplicationContext.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[] { WebMvcContext.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

    // ... onStartup etc.

}

Поддержка вебмвкконфигуратион:

@Configuration
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
@PropertySource({ "classpath:config.properties", "file:${CONFIGDIR}/config.properties" })
@ComponentScan(useDefaultFilters = false, basePackages = { "com.yyy", "com.xxx" }, includeFilters = { @Filter(type = FilterType.ANNOTATION, value = Controller.class) })
public class WebMvcContext extends WebMvcConfigurationSupport {

    // ... beans etc.
}

Контекст приложения:

@Configuration
@EnableAsync
@EnableScheduling
@EnableMBeanExport
@Import({SecurityConfig.class, GeneralDBConfiguration.class})
@ComponentScan(useDefaultFilters = true, basePackages = { "com.yyy", "com.xxx" }, excludeFilters = { @Filter(type = FilterType.ANNOTATION, value = {Controller.class, Configuration.class/*, Aspect.class*/}) })
public class ApplicationContext implements AsyncConfigurer {

    // beans etc.

}

Как мне включить часть классов конфигурации io.swagger.configuration в мое существующее приложение?

Подробнее:

Одна из проблем, с которыми я сталкиваюсь, заключается в том, что если я укажу зависимость maven от заглушек зоомагазина (которая устанавливается локально, запустив mvn install:install-file ... из каталога spring-mvc-j8-async):

    <dependency>
        <groupId>io.swagger</groupId>
        <artifactId>swagger-spring-mvc-server</artifactId>
        <version>1.0.0</version>
    </dependency>

Затем мое весеннее приложение находит два AbstractAnnotationConfigDispatcherServletInitializer (один из моего приложения и io.swagger.configuration.WebApplication из swagger-spring-mvc-server) и не загружается, что дает следующее исключение:

Не удалось зарегистрировать сервлет с именем «диспетчер». Проверьте, не зарегистрирован ли другой сервлет под тем же именем.

Я думаю, мой вопрос можно сформулировать по-другому: как вы используете серверные заглушки, сгенерированные swagger-codegen? Похоже, я не могу просто зависеть от пакета maven из коробки...


person jlb    schedule 20.04.2016    source источник
comment
Похоже, вы используете стек Spring mvc, а не стек max-Es. Я бы рекомендовал использовать spring- вместо этого образец mvc.   -  person Dilip Krishnan    schedule 21.04.2016
comment
@DilipKrishnan, что ты имеешь в виду под max-Es? Заглушка зоомагазина, с которой я пытаюсь интегрироваться, — это spring-mvc-j8-async - и фактически пакет io.swagger.configuration идентичен пакету в простом spring-mvc заглушка.   -  person jlb    schedule 21.04.2016
comment
Это была опечатка, я имел в виду JAX-RS. Не уверен насчет другого образца (я написал весеннюю версию mvc). Наверняка отсутствует аннотация @enableSwagger2.   -  person Dilip Krishnan    schedule 21.04.2016
comment
@DilipKrishnan спасибо - очень признателен. Я думаю, что я борюсь с более серьезной проблемой - см. Дополнительный раздел, добавленный в конце вопроса.   -  person jlb    schedule 21.04.2016
comment
почему бы не использовать springfox   -  person ali akbar azizkhani    schedule 23.03.2017
comment
@jlb вы когда-нибудь находили решение этой ошибки: вызвано: java.lang.ClassNotFoundException: org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer?   -  person Bass    schedule 15.03.2020
comment
@Bass это было давно - к сожалению не помню :(   -  person jlb    schedule 16.03.2020


Ответы (1)


Я вижу, что это довольно старый, но я много боролся с этим, и собранная мной информация может быть полезна другим, пока документы генератора не будут улучшены.

Это описание предназначено для создания и использования заглушки сервера spring-mvc из спецификации OpenApi 3.0.0 с использованием плагина openapi-generator-maven.

  1. Не используйте swagger-codegen, вместо этого используйте генератор openapi: https://github.com/OpenAPITools/openapi-generator (обоснование форка здесь: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/qna.md)

  2. Создайте отдельный проект/модуль для сервера и настройте плагин maven.

<build>
    <plugins>
        <plugin>
            <groupId>org.openapitools</groupId>
            <artifactId>openapi-generator-maven-plugin</artifactId>
            <version>3.3.4</version>
            <executions>
                <execution>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                    <configuration>
                        <skipIfSpecIsUnchanged>true</skipIfSpecIsUnchanged>
                        <inputSpec>${engine-openapi-spec.location}</inputSpec>
                        <output>${project.build.directory}/generated-sources/openapi</output>
                        <generatorName>spring</generatorName>
                        <library>spring-mvc</library>
                        <apiPackage>eu.dorsum.swift.engine.service.api</apiPackage>
                        <modelPackage>eu.dorsum.swift.engine.service.model</modelPackage>
                        <generateApis>true</generateApis>
                        <generateApiDocumentation>false</generateApiDocumentation>
                        <generateApiTests>false</generateApiTests>
                        <generateModels>true</generateModels>
                        <generateModelDocumentation>false</generateModelDocumentation>
                        <generateModelTests>false</generateModelTests>
                        <generateSupportingFiles>true</generateSupportingFiles>
                        <configOptions>
                            <sourceFolder>src/main/java</sourceFolder>
                            <java8>true</java8>
                            <dateLibrary>java8</dateLibrary>
                            <useTags>true</useTags>
                            <configPackage>eu.dorsum.swift.engine.appconfig</configPackage>
                            <interfaceOnly>false</interfaceOnly>
                            <delegatePattern>true</delegatePattern>
                        </configOptions>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Есть две части конфигурации, которые довольно сложно понять.

2.a: Установите configOptions/configPackage для пакета, в котором находится корневая конфигурация вашего приложения. Эта опция добавит ваш пакет конфигурации в качестве дополнительного basePackage для сканирования компонентов в OpenAPIUiConfiguration.java: @ComponentScan(basePackages = {"eu.dorsum.swift.engine.service.api", "eu.dorsum.swift.engine.appconfig"}). Это перевернутый подход, о котором вы могли подумать изначально, когда сгенерированная конфигурация mvc запускает ваши существующие вещи, но это единственный способ, который я нашел, не требующий изменений в сгенерированном коде.

2.b: Установите configOptions/delegatePattern в значение true. Этот мне очень нравится! Это создаст дополнительный интерфейс делегирования, который может реализовать ваш серверный контроллер. Сгенерированный ApiController делегирует все вызовы службы этому интерфейсу, так что вы можете очень элегантно подключить свою реализацию. В моей настройке у меня есть эта цепочка: MessageApi (сгенерированный интерфейс) -> MessageApiController реализует MessageApi (сгенерированный контроллер mvc) -> MessageApiDelegate (сгенерированный интерфейс) -> MessageService реализует MessageApiDelegate (моя реализация методов службы).

Имея эти две части конфигурации, нет необходимости изменять сгенерированные исходники. Если спецификация API изменяет интерфейс делегирования, вы должны реализовать эти изменения в MessageService.

Ниже приведено обновление моего исходного сообщения, в котором более подробно объясняется, как реализация службы связана с интерфейсом делегата.

Исходники, созданные openapi-gen, находятся в отдельном модуле jar, содержащем только swagger.yaml и pom.xml в качестве зарегистрированных «источников». Модуль, использующий сгенерированный код, — это война, которая имеет прямую зависимость от сгенерированного jar. Реализация службы находится в состоянии войны и реализует интерфейс MessageApiDelegate, который находится в сгенерированном источнике. Контекст Spring извлекается из сгенерированного кода, и пакеты, которые вы определяете как apiPackage и configPackage в конфигурации openapi-gen, будут добавлены как basePackages для @ComponentScan в сгенерированном OpenAPIUiConfiguration.java, поэтому реализация вашей службы должна быть помещена в или под apiPackages.

Вот упрощенная структура, чтобы собрать части вместе.

parent/
  swift-engin-service/ (generated jar module)
    swagger.yaml
    pom.xml
    target/generated-sources/openapi/src/main/java/eu/dorsum/swift/engine/
      appconfig/ (generated spring webmvc config)
        OpenAPIUiConfiguration.java
        WebApplication.java (brings up spring context by extending AbstractAnnotationConfigDispatcherServletInitializer)
      service/
        MessageApiController.java (@Controller, @RequestMapping etc.)
        MessageApiDelegate.java (implement this to get your service implementation plugged in the controller)
  swift-engine/ (war module, your code)
    pom.xml
    src/main/java/eu/dorsum/swift/engine/
      appconfig/
        EngineConfig.java (my spring config)
      service/api/ (must be the same as apiPackages property)
        MessageService.java (service implementation)

Соответствующая часть swift-engine/pom.xml

<project>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>eu.dorsum.core.java.swift-engine</groupId>
            <artifactId>swift-engine-service</artifactId>
        </dependency>
    </dependencies>
</project>

Соответствующая часть eu.dorsum.swift.engine.service.api.MessageService

@Service
public class MessageService implements MessageApiDelegate {
    @Override
    public ResponseEntity<List<SwiftMessage>> listMessages(List<String> sorting, Integer pageStart, Integer pageSize, String filter) {
        // implementation
    }

    @Override
    public ResponseEntity<Void> updateMessage(SwiftMessage message) {
        // implementation
    }
}

Соответствующая часть eu.dorsum.swift.engine.appconfig.OpenAPIUiConfiguration (сгенерирована)

@Configuration
@ComponentScan(basePackages = {"eu.dorsum.swift.engine.service.api", "eu.dorsum.swift.engine.appconfig"})
@EnableWebMvc
public class OpenAPIUiConfiguration extends WebMvcConfigurerAdapter {
    ...
}
person Peter    schedule 25.02.2019
comment
Кажется, я не могу вызвать реализованные методы. Вместо этого методы по умолчанию в интерфейсе все еще вызываются, хотя мой аннотированный класс обслуживания реализует методы интерфейса (по умолчанию). Чего-то не хватает на моем конце? - person Bass; 07.03.2020
comment
Скорее всего, да :) Вы также используете spring-mvc? - person Peter; 08.03.2020
comment
Делаю Питер вместе с Spring 5.1.1. Я пробовал разные комбинации настроек: одну с openapi с/без springboot, другую в том же модуле, что и мое приложение springmvc, и все с weblogic в качестве сервера приложений. У вас есть отдельная война для модуля openapi? Как модуль openapi видит MessageService, я полагаю, все в одном модуле? Не могли бы вы также опубликовать свой pom.xml для модуля openapi? Я использую плагин open-api-generator 4.2.3. - person Bass; 09.03.2020
comment
delegatePattern, безусловно, самый важный. Я уже думал генерировать только интерфейсы, пока не увидел ваш пост. Большое спасибо! - person LionH; 10.03.2020
comment
Bass, извините за поздний ответ, я не мог найти время ответить раньше. Я отредактировал свой ответ с деталями, надеюсь, это поможет. - person Peter; 15.03.2020