REST API在业界的应用越来越普及,大量的规范、可复用的基础设施、新工具,让基于REST API的开发体验非常好。Azure、GCP等大型云计算服务基于REST API规范提供成千上万个API,足以体现REST API丰富的表达能力。
鉴于REST API的优秀特性,所以OpenAPI使用REST规范来构建。OAI致力于OpenAPI的标准化。微软和Google等巨头都是OAI的成员。2017年7月OAI发布了OAS 3.0。
OAS 3.0是基于Swagger 2.0规范改进而来的,所以使用Swagger的工具链来做OpenAPI开发是一个不错的选择。Swagger提了一个PetStore的demo供用户体验。
接入配置
首先引入依赖。
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
接着在Application同级目录创建下面这个类即可打开Swagger。设置@Profile("dev")
,用以控制在测试环境才打开Swagger。
@Profile("dev")
@Configuration
@EnableSwagger2
public class Swagger2 {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.aliyun.ram.sdk.validate.control.ramsdkvalidatecontrol.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
Contact contact = new Contact("阿呆少爷", "https://1.2.3.4", "xxx@xxx.com");
return new ApiInfoBuilder()
.title("TaskManager")
.description("TaskManager")
.contact(contact)
.version("1.0")
.build();
}
}
打开http://localhost:8080/swagger-ui.html,即可看到swagger的界面。界面上可以看到Controller和Model。在Swagger-UI里面可以测试API,挺方便的。
Swagger注解
Swagger针对接口和实体类提供很多注解,用于做更加精细的描述。
@ApiOperation(value = "Add a new pet to the store", nickname = "addPet", notes = "", authorizations = {
@Authorization(value = "petstore_auth", scopes = {
@AuthorizationScope(scope = "write:pets", description = "modify pets in your account"),
@AuthorizationScope(scope = "read:pets", description = "read your pets")
})
}, tags={ "pet", })
@ApiResponses(value = {
@ApiResponse(code = 405, message = "Invalid input") })
@RequestMapping(value = "/pet",
produces = { "application/xml", "application/json" },
consumes = { "application/json", "application/xml" },
method = RequestMethod.POST)
ResponseEntity<Void> addPet(@ApiParam(value = "Pet object that needs to be added to the store" ,required=true ) @Valid @RequestBody Pet body);
@ApiOperation(value = "Find pet by ID", nickname = "getPetById", notes = "Returns a single pet", response = Pet.class, authorizations = {
@Authorization(value = "api_key")
}, tags={ "pet", })
@ApiResponses(value = {
@ApiResponse(code = 200, message = "successful operation", response = Pet.class),
@ApiResponse(code = 400, message = "Invalid ID supplied"),
@ApiResponse(code = 404, message = "Pet not found") })
@RequestMapping(value = "/pet/{petId}",
produces = { "application/xml", "application/json" },
method = RequestMethod.GET)
ResponseEntity<Pet> getPetById(@ApiParam(value = "ID of pet to return",required=true) @PathVariable("petId") Long petId);
即使不使用注解,Swagger依旧对Java支持良好,比如识别枚举类型。
@ApiModel
public class TaskStatus {
@ApiModelProperty(value = "任务ID")
private Long id;
@ApiModelProperty("任务运行状态")
private TaskWorkStatus status;
@ApiModelProperty("任务详细状态")
@Size(max = 5120)
private String detailStatusJson;
}
生成代码
根据规范生成各种语言的SDK是一个大大的福利。可以在https://editor.swagger.io/里面生成代码,或者在终端使用swagger-codegen
生成代码。
$ brew install swagger-codegen
$ export package_name=xxx
$ swagger-codegen generate -i api.json -l java -o /tmp/ramsdk/ --api-package ${package_name}.api --model-package ${package_name}.model --invoker-package ${package_name}
服务器端
Java
我们来分析一下Swagger生成的Java Spring的代码。Swagger生成的代码支持XML和JSON两种格式的输入和输出,考虑很周全。
代码结构很清晰,接口描述都在PetApi接口里面,PetApiController实现PetApi接口。具体的业务逻辑需要自己实现Service层来做。
客户端
Java
Swagger生成的Java客户端使用OkHttp调用服务器端API,并且提供同步和异步的接口。
ApiClient apiClient = new ApiClient();
apiClient.setBasePath(task.getServiceEndpoint());
apiClient.setDebugging(true);
ControllerApi controllerApi = new ControllerApi();
controllerApi.setApiClient(apiClient);
//同步调用
ApiResponse<Task> response = controllerApi.taskUsingPOSTWithHttpInfo(createTaskRequest);
//异步调用
//api.taskUsingPOSTAsync(createTaskRequest, callback);
return response.getData();
Java客户端提供debug开关,打开之后可以看到HTTP的收发信息。
React
使用swagger-codegen生成typescript-fetch
代码,使用标准的Fetch API,调用服务器端接口。
React项目中,import生成的代码,就可以通过ControllerApi请求相应的API,非常方便。使用Ant Design的表格显示任务列表。
import { Table } from 'antd';
import * as React from 'react';
import { Task, TaskControllerApi } from './api/ram-sdk-validate-control'
import './App.css';
interface IState {
tasks: Task[],
}
class App extends React.Component<any, IState> {
constructor(props: any) {
super(props);
this.state = {
tasks: [],
}
}
const taskControllerApi = new TaskControllerApi();
taskControllerApi.listTasksUsingGET().then(tasks => {
this.setState({
tasks
});
});
}
public render() {
const { tasks } = this.state;
const columns = [
{
dataIndex: 'id',
key: 'id',
title: 'ID',
},
{
dataIndex: 'name',
key: 'name',
title: '任务名称',
},
];
return (
<div className="App">
<Table
dataSource={ tasks }
columns={ columns }
rowKey="id"
/>
</div>
);
}
}
export default App;
NG-ZORRO
在Swagger Editor生成代码,选择typescript-angular
。将代码下载、解压、拖入工程。在app.module.ts里面增加模块的配置及注入方式。
import {ApiModule, Configuration, ConfigurationParameters} from './ram-sdk-validate'
export function apiConfigFactory (): Configuration {
const params: ConfigurationParameters = {
basePath: "http://10.101.90.71:8080/"
}
return new Configuration(params);
}
@NgModule({
declarations : [
AppComponent,
TodoComponent,
TodoMenuComponent,
TodoMenuChildComponent,
TodoModalEditComponent
],
imports : [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
HttpClientModule,
AppRoutingModule,
NgZorroAntdModule,
ApiModule.forRoot(apiConfigFactory)
],
entryComponents: [
TodoModalEditComponent
],
providers : [ { provide: NZ_I18N, useValue: zh_CN } ],
bootstrap : [ AppComponent ]
})
export class AppModule {
}
然后就可以在component中使用模块了。
import { Task, TaskControllerService } from '../ram-sdk-validate'
taskService.listTasksUsingGET().subscribe(tasks =>
{
this.tasks = tasks;
}
)
tasks: Task[];
使用Ant Design的表格显示任务列表。
<nz-table #basicTable [nzData]="tasks">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of basicTable.data">
<td>{{data.id}}</td>
<td>{{data.name}}</td>
<td>
<a>Pause</a>
<a>Delete</a>
</td>
</tr>
</tbody>
</nz-table>
更正规的做法是将client发布到仓库,然后在项目中引入相关的依赖。发布模块请参考:将Swagger生成的Angular client发布到npm仓库。
性能问题
一般管控API不会太在意性能,但是业务API,比如消息服务的API,调用频繁,对RT也有要求,所以使用HTTP协议时性能会是一个问题。为了解决性能问题,GCP还支持gRPC协议。
Multiple surfaces: REST and gRPC
All our Cloud APIs expose a simple JSON REST interface that you can call directly or via our client libraries. Some of our latest generation of APIs also provide an RPC interface that lets clients make calls to the API using gRPC: many of our client libraries use this to provide even better performance when you use these APIs. You can find out more about API interfaces and library types in Client Libraries Explained.
Regardless of interface type, Cloud APIs use resource-oriented design principles as described in our Google API Design Guide.
考虑到用户测试方便和复杂的需求,可以通过gRPC REST Gateway以提供REST API。
一点感悟
云计算的本质就是API。云产品通过API提供服务能力。控制台和SDK均使用同一套API。OpenAPI应运而出,让业界迅速对API设计规范达成广泛的共识,并且产生了一批优秀的工具,帮助大家高效率设计和实现API。
OpenAPI基于HTTP协议,而OAuth 2.0同样也基于HTTP协议,所以REST API+OAuth 2.0是最佳组合。云计算的后起之秀,比如Azure和GCP都采用了这套解决方案。
未来的开发模式就是先定义好API,然后生成服务器端的框架,服务开发完成之后,自动生成各种SDK提供给客户使用。