作者:Tony Tam
Swagger Inflector 是 Swagger 团队的一个新项目,用于在 JVM 上编写 API。它目前处于预览状态,但正在积极开发和支持,作为在 Java 中编码 API 的替代方案,即设计优先的方法。让我们来看看如何使用 Inflector 项目。
[embed]http://www.slideshare.net/Swagger-API/the-inflector-project[/embed]
入门
首先,我们从 Swagger 规范开始。您可以使用您喜欢的文本编辑器以 JSON 或 YAML 格式创建规范,或者使用 http://editor.swagger.io 上精彩的在线编辑器来构建 API 的定义。不用担心进行更改!我们将介绍 Inflector 如何帮助您快速迭代您的设计,而无需编写任何代码。
在本演示中,我们假设您已从 此处复制了 Swagger 描述。
接下来,我们创建一个 YAML 文件来配置 Inflector。这允许使用许多选项,但现在,我们只放置 Swagger 描述的位置
# inflector.yaml
swaggerUrl: ./src/main/swagger/swagger.yaml
由于 Inflector 在底层使用了 Swagger Parser,因此您可以指向本地或远程文件。如果您托管您的定义,只需指定 http
或 https
协议,Swagger Parser 将检索它。我们将在以后的博客文章中介绍此功能的一些非常有趣的用例。
现在让我们添加 Inflector 依赖项。在运行时,Inflector 只是使用 Jersey 2.6 作为 REST 框架,因此有很多用于生产集成的选项。现在,让我们使用 Jetty Maven 插件并将 Inflector 添加为依赖项
<build>
<plugins>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.2.9.v20150224</version>
<configuration>
<monitoredDirName>.</monitoredDirName>
<scanTargets>
<scanTarget>inflector.yaml</scanTarget>
<scanTarget>src/main/swagger/swagger.yaml</scanTarget>
</scanTargets>
<scanIntervalSeconds>1</scanIntervalSeconds>
<webApp>
<contextPath>/</contextPath>
</webApp>
<httpConnector>
<port>8080</port>
<idleTimeout>60000</idleTimeout>
</httpConnector>
</configuration>
</plugin>
<!-- 您的其他插件 -->
</plugins>
</build>
<dependencies>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-inflector</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<!-- 您的其他依赖项 -->
查看示例 pom.xml,其中包含您需要的一切。
请注意,我们在插件配置中配置了两个扫描目标。如果 ./inflector.yaml
或 src/main/swagger/swagger.yaml
描述文件发生更改,这将告诉 Jetty 重新启动应用程序。您将看到这如何使 Inflector 的开发变得轻而易举。
最后,让我们添加一个 web.xml
。这使我们可以在 Jersey 上下文中,在服务的根目录中挂载 Inflector 应用程序。我们还将添加一个 CORS 过滤器,以便我们可以轻松地从 Swagger UI 读取我们的 Swagger API。
<!-- from https://github.com/swagger-api/swagger-inflector/blob/master/samples/jetty-webxml/src/main/webapp/WEB-INF/web.xml -->
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>swagger-inflector</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>io.swagger.inflector.SwaggerInflector</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>swagger-inflector</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>CORSFilter</filter-name>
<filter-class>io.swagger.inflector.utils.CORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CORSFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
请注意我们如何添加 CORS 过滤器。这就像任何标准的 web.xml,您可以添加过滤器、静态 servlet 等。这里没有魔法!
这就是我们的项目设置。现在让我们试驾一下。
启动服务器
mvn package jetty:run
并在端口 8080
(在 jetty 插件中配置)上读取 swagger 列表。根据您在 Swagger 描述中配置的 basePath
,您现在可以在 https://#:8080/{basePath}/swagger.json
打开 Swagger 描述(当然,根据规范,可以省略 basePath)。这将以 JSON 格式显示您一直在编辑的规范。我知道,没什么大不了的,但我们才刚刚开始。
请注意,根据我们的 Jetty 插件配置,您可以将更改保存到您的 swagger.yaml
文件并进行自动重新加载(它每秒扫描一次)。一个重要提示!如果您编写了无效的规范,服务器将拒绝提供它。使用 Swagger Editor 获取有关编写有效 Swagger 描述的上下文相关帮助!
现在是精彩的部分。让我们从我们的描述中取出一个路由
/pet/{petId}:
get
tags
- pet
summary: 通过 ID 查找宠物
description: 返回单个宠物
operationId: getPetById
consumes
- application/x-www-form-urlencoded
produces
- application/xml
- application/json
parameters
- name: petId
in: path
description: 要返回的宠物的 ID
required: true
type: integer
format: int64
responses
"200":
description: 操作成功
schema
$ref: "#/definitions/Pet"
"400":
description: 提供的 ID 无效
"404":
description: 未找到宠物
如果您访问此端点(https://#:8080/v2/pet/1
),Inflector 将根据您的 Accepts
标头,以 JSON 或 XML 格式提供 #/definitions/Pet
模型的示例有效负载。无需代码!Inflector 将根据 swagger 模式构建示例
{
"id": 0,
"category": {
"id": 0,
"name": "string"
},
"name": "小狗",
"photoUrls": [
"string"
],
"tags": [
{
"id": 0,
"name": "string"
}
],
"status": "string"
}
这很有趣!您可以对 Swagger 描述中的每个操作尝试相同的方法。Inflector 将使模拟服务变得简单。同样,当您修改服务描述并进行更改时,Jetty 插件将重新加载并立即显示您的更改。您甚至可以加载 swagger-ui 并指向我们的 Swagger 描述以查看真实内容。
现在,让我们超越 Hello World,让它更真实。假设您不想要模拟响应,而是想实现一个控制器并放入一些业务逻辑。Inflector 将通过将请求直接路由到控制器来简化此过程。
这通过多种技术进行配置。让我们来看看这些选项。
显式命名控制器 + 包
您可以使用 Swagger Extension
在您的描述中添加其他元数据。对于此用例,我们将添加我们要将请求发送到的控制器类的名称
/pet:
post
tags
- pet
summary: 向商店添加一只新宠物
operationId: addPet
# 我们正在配置 Inflector 以在此类中查找我们的控制器!
x-swagger-router-controller: io.swagger.sample.SampleController
现在,在启动时,Inflector 将在 io.swagger.sample.SampleController
类中查找与此方法匹配的方法。但是我们的签名是什么???
Inflector 将使用 operationId
作为方法名。如果它不存在,则会使用一种算法来创建一个(您应该指定它以简化操作)。然后,它会查找与操作的输入参数匹配的参数签名,外加一个额外的上下文参数。实际上,如果您配置应用程序以启动调试日志记录,您会看到它正在尝试查找的内容。
DEBUG i.s.i.SwaggerOperationController - looking for operation: addPet(io.swagger.inflector.models.RequestContext request, com.fasterxml.jackson.databind.JsonNode body)
请注意,RequestContext
作为第一个参数——它包含有关标头、媒体类型等的宝贵信息。查看源代码,了解它为您提供了什么。
请注意,body
直接映射到 JsonNode
对象。这为您提供了一个 Map
或 Array
类型的对象,您可以将其视为您的领域模型(这对于 XML 也成立)。
自动定位命名控制器 + 包
对于勇敢者,您可以配置 Inflector 来扫描 controllerPackage
# inflector.yaml
controllerPackage: io.swagger.sample.controllers
Inflector 将始终在此处查找。您还可以使用 controllerPackage
设置默认包查找,并设置 x-swagger-router-controller: SampleController
而不是使用 FQ 名称。
然后,inflector 将使用第一个标签来构造类名。如果您有一个标记为以下内容的操作
tags:
- pet
Inflector 现在将 controllerPackage
+ ‘.’ + Capitalize(tags[0]) + Controller
作为预期的类名
io.swagger.sample.controllers.PetController
实现业务逻辑
实现业务逻辑就像在我们的控制器中创建该方法一样简单
package io.swagger.sample;
import io.swagger.inflector.models.RequestContext;
import io.swagger.inflector.models.ResponseContext;
import com.fasterxml.jackson.databind.JsonNode;
import javax.ws.rs.core.Response.Status;
public class SampleController {
// ResponseContext 是可选的,但它可以提供关于写入客户端的额外控制
public ResponseContext addPet(RequestContext request, JsonNode body) {
return new ResponseContext()
.status(Status.OK)
.entity(body);
}
}
这很无聊,是的,但这表明我们现在可以完全控制响应对象,以及 Inflector 如何直接映射到我们的控制器!当您不用忙于处理所有管道时,您会发现编写 REST API 的速度非常惊人!
映射 POJO
假设我们想要一个具体的对象,而不是使用 JsonNode
。这很容易做到——您有两种选择。
首先,您可以使用 modelMappings
语法在 inflector.yaml
中直接映射模型
modelMappings:
Pet: io.swagger.sample.models.Pet
您猜对了,当我们在 Swagger 描述中看到 Pet
定义时,我们将使用 io.swagger.sample.models.Pet
实现。
其次,您可以配置 modelPackage
,Inflector 将尝试查找它
modelPackage: io.swagger.sample.models
这将导致 Inflector 从类加载器加载 io.swagger.sample.models.Pet
。如果在两种情况下都存在,则您的方法签名将看起来更加友好
DEBUG i.s.i.SwaggerOperationController - looking for operation: addPet(io.swagger.inflector.models.RequestContext request, io.swagger.sample.models.Pet body)
现在您可以开始使用更熟悉的代码了
public ResponseContext addPet(RequestContext request, Pet body) {
/* 所有业务逻辑 */
}
结论
这是一篇相当长的文章,但希望 Inflector 的设计优先目标是明确的。一旦您可以在不担心所有管道的情况下开始编写 API,您会发现您将创建更好的 API,并且速度更快。Inflector 消除了生成 Swagger 描述所需的注解和其他连线,并将 Swagger 本身置于 REST API 的 DSL 角色中。