与时俱进:在JAX采用开放源码软件3.0.0版


看到时间过得如此之快真是令人害怕!这OpenAPI specification 3.0.0是对“习惯”的重大改进Swagger specification,大部分是在一年前发布的,但是工具需要一段时间才能赶上。然而,随着最近官方发布的Swagger Core 2.0.0事情肯定会加速。

为了证明这一点,Apache CXF,众所周知JAX-RS 2.1实现,是OpenAPI 3.0.0在今天的帖子中,我们将看看你的JAX-RS 2.1API可以从中受益。

一如既往,为了简单起见,我们将设计一个人员管理网络应用编程接口,仅用少量资源来支持它,这里没有什么太令人兴奋的。

POST   /api/people
GET    /api/people/{email}
GET    /api/people
DELETE /api/people/{email}

我们的模型将由一个人类组成。

public class Person {
    private String email;
    private String firstName;
    private String lastName;
}

为了增加一点魔力,我们将使用Spring Boot让我们尽可能快地启动和运行。这样,让我们开始填充依赖关系(假设我们正在使用Apache Maven用于构建管理)。

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-spring-boot-starter-jaxrs</artifactId>
    <version>3.2.4</version>
</dependency>

在最近的3.2.x版本中,Apache CXF介绍了一个新模块cxf-rt-RS-service-description-openapi-v3,专用于OpenAPI 3.0.0,基于Swagger Core 2.0.0

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-rs-service-description-openapi-v3</artifactId>
    <version>3.2.4</version>
</dependency>

<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>swagger-ui</artifactId>
    <version>3.13.6</version>
</dependency>

的存在Swagger UI并不是绝对必要的,但是这是一个非常有用和漂亮的工具来探索您的API(如果它在类路径上可用,Apache CXF如我们稍后将看到的那样,将它无缝集成到您的应用程序中)。

先决条件已经到位,让我们做一些编码!在我们开始之前,值得注意的是Swagger Core 2.0.0有许多方法来填充OpenAPI 3.0.0服务的定义,包括属性文件、注释或编程方式。在这篇文章中,我们将只使用注释。

@OpenAPIDefinition(
    info = @Info(
        title = "People Management API",
        version = "0.0.1-SNAPSHOT",
        license = @License(
            name = "Apache 2.0 License",
            url = "http://www.apache.org/licenses/LICENSE-2.0.html"
        )
    )
)
@ApplicationPath("api")
public class JaxRsApiApplication extends Application {
}

看起来很简单,@OpenAPIDefinition为我们所有的网络应用编程接口设置了顶级定义。接下来是PeopleRestService,我们只是在我们的应用编程接口上添加了@Tag注释。

@Path( "/people" ) 
@Tag(name = "people")
public class PeopleRestService {
    // ...
}

太棒了,目前还没什么复杂的。最重要的部分从网络应用编程接口操作定义开始,所以让我们看看第一个例子,获取每个人的操作。

@Produces(MediaType.APPLICATION_JSON)
@GET
@Operation(
    description = "List all people", 
    responses = {
        @ApiResponse(
            content = @Content(
                array = @ArraySchema(schema = @Schema(implementation = Person.class))
            ),
            responseCode = "200"
        )
    }
)
public Collection<Person> getPeople() {
    // ...
}

相当多的注释,但是,总的来说,看起来非常干净和简单。让我们看一看另一个端点,通过他们的电子邮件地址找到这个人。

@Produces(MediaType.APPLICATION_JSON)
@Path("/{email}")
@GET
@Operation(
    description = "Find person by e-mail", 
    responses = {
        @ApiResponse(
            content = @Content(schema = @Schema(implementation = Person.class)), 
            responseCode = "200"
        ),
        @ApiResponse(
            responseCode = "404", 
            description = "Person with such e-mail doesn't exists"
        )
    }
)
public Person findPerson(
        @Parameter(description = "E-Mail address to lookup for", required = true) 
        @PathParam("email") final String email) {
    // ...
}

同样,通过电子邮件将人转移的操作看起来也差不多。

@Path("/{email}")
@DELETE
@Operation(
    description = "Delete existing person",
    responses = {
        @ApiResponse(
            responseCode = "204",
            description = "Person has been deleted"
        ),
        @ApiResponse(
            responseCode = "404", 
            description = "Person with such e-mail doesn't exists"
        )
     }
)
public Response deletePerson(
        @Parameter(description = "E-Mail address to lookup for", required = true ) 
        @PathParam("email") final String email) {
    // ...
}

太好了,让我们来看一下可以说是最有趣的添加新人的端点(选择使用@FormParam纯粹是为了说明APIs的不同风格)。

@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON)
@POST
@Operation(
    description = "Create new person",
    responses = {
        @ApiResponse(
            content = @Content(
                schema = @Schema(implementation = Person.class), 
                mediaType = MediaType.APPLICATION_JSON
            ),
            headers = @Header(name = "Location"),
            responseCode = "201"
        ),
        @ApiResponse(
            responseCode = "409", 
            description = "Person with such e-mail already exists"
        )
    }
)
public Response addPerson(@Context final UriInfo uriInfo,
        @Parameter(description = "E-Mail", required = true) 
        @FormParam("email") final String email, 
        @Parameter(description = "First Name", required = true) 
        @FormParam("firstName") final String firstName, 
        @Parameter(description = "Last Name", required = true) 
        @FormParam("lastName") final String lastName) {
    // ...
}

如果你有过这样的经历documenting web APIs using older Swagger specifications,您可能会发现这种方法非常熟悉,但更加冗长(或者说“形式化”更好)。它是规范所领导的巨大工作的结果,并且社区已经尽可能地使它完整和可扩展。

API已经被定义和记录,所以现在是时候尝试一下了!然而,缺失的部分是Spring我们将初始化并公开我们的JAX-RSweb服务。

@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackageClasses = PeopleRestService.class)
public class AppConfig {
    @Autowired private PeopleRestService peopleRestService;

    @Bean(destroyMethod = "destroy")
    public Server jaxRsServer(Bus bus) {
        final JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();

        factory.setApplication(new JaxRsApiApplication());
        factory.setServiceBean(peopleRestService);
        factory.setProvider(new JacksonJsonProvider());
        factory.setFeatures(Arrays.asList(new OpenApiFeature()));
        factory.setBus(bus);
        factory.setAddress("/");

        return factory.create();
    }

    @Bean
    public ServletRegistrationBean cxfServlet() {
        final ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new CXFServlet(), "/api/*");
        servletRegistrationBean.setLoadOnStartup(1);
        return servletRegistrationBean;
    }
}

OpenApiFeature是这里的一个关键要素,它负责所有的集成和内省。这Spring Boot应用程序是完成图片的最后一步。

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(AppConfig.class, args);
    }
}

让我们立即构建并运行它:

mvn clean package 
java -jar target/jax-rs-2.1-openapi-0.0.1-SNAPSHOT.jar

应用程序启动后OpenAPI 3.0.0我们的网络应用编程接口规范应该是实时的,并且可以在JSON格式位于:

http://localhost:8080/api/openapi.json

或者在YAML格式位于:

http://localhost:8080/api/openapi.json

探索我们的网络应用编程接口并和它们一起玩不是很棒吗?因为我们包括了一个Swagger UI依赖,这是显而易见的,只需导航到http://localhost:8080/API/API-docs?url=/api/openapi.json

应该特别注意小图标Swagger UI放置在您的应用编程接口版本旁边,暗示它符合OpenAPI 3.0.0规格。

请注意,使用没有什么特别之处Spring Boot。以防你使用Apache CXF在里面OSGi容器(如Apache Karaf,例如),与OpenAPI 3.0.0也可以使用(请查看official documentation and samples如果你对这个主题感兴趣的话)。

看起来很简单,但是迁移到呢OpenAPI 3.0.0旧版本的Swagger规格?这Apache CXF具有强大的功能convert older specifications on the fly,但是,总的来说OpenApi.Tools门户是评估您的选项的正确位置。

你应该迁移到OpenAPI 3.0.0?我真诚地认为你应该尝试,或者至少你应该尝试一下,但是请注意,工具还不成熟,所以在前进的道路上要有一些障碍(顺便说一下,你可以通过贡献补丁来克服这些障碍!)。但毫无疑问,未来是光明的!

完整的项目来源可从以下网站获得GitHub