你随意给我一个用openapi定义的接口文档,我不动代码就能调用你的接口

最近在思考构建一个服务编排(Service Orchestration)系统,考虑这个系统至少需要具备以下特征:

  1. 使用统一的方法定义服务功能单元
  2. 使用一种通用的方式将一个或多个服务的输出映射到下游服务的输入,映射时支持基础的数据转换与处理
  3. 支持以搭积木的方式将低层服务功能单元组织成更高层抽象的服务功能,直至一个完整的服务
  4. 用户编排服务时,具备较大的灵活性定制业务

1 OpenAPI介绍

系统与系统之间、系统内各组件之间通过http API进行通信的情况非常普遍,于是有人专门制定了相应的规范OpenAPI Specification(OAS),一般简称为OpenAPI,以前被称为Swagger。这个规范规定了RESTful API从设计、构建、文档到使用的方方面面,而且是人类和计算机方便理解的,还是与开发语音无关的,当前已然成为定义与描述http API的标准。围绕这个规范,大家开发了很多相关工具,比如用于OpenAPI文档开发的Swagger Editor,用于生成、可视化和测试OpenAPI的Swagger UI。另外还有SwaggerHub、SpringDoc、ApiFox、Knife4j、postman等等开源或商业产品。对于支持OpenAPI的java语言工具来说,都会使用到swagger-parser这个底层组件,下面简单的介绍一下这个工具的基本使用。

2 基于OpenAPI文档调用http接口

假设某个服务提供了下面这个接口:

java 复制代码
public class HelloRequest {
    private String content;
}

public class HelloResponse {
    private String content;
}

@RestController
@RequestMapping(value = "/")
public class Controller {

    @PostMapping(value = "/test")
    public HelloResponse test(@RequestBody HelloRequest request) {
        return new HelloResponse("echo: " + request.getContent());
    }
}

那么我们就可以基于OpenAPI规范使用以下json文档来描述这个接口:

json 复制代码
{
  "openapi": "3.1.0",
  "info": {
    "title": "test openapi",
    "version": "1.0.0"
  },
  "paths": {
    # 接口路径
    "/test": {
      # 接口方法
      "post": {
        "operationId": "openapi-test",
        # 接口请求body
        "requestBody": {
          "content": {
            # 接口请求body格式
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  # request body有一个类型为string,名字为content的字段
                  "content": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          # 接口请求成功时的返回内容
          "200": {
            "description": "Successful operation",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "content": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

也可以用yaml文档进行等价描述,它看上去更简洁些:

yaml 复制代码
openapi: 3.1.0
info:
  title: test openapi
  version: 1.0.0
servers:
- url: /
paths:
  /test:
    post:
      operationId: openapi-test
      requestBody:
        content:
          application/json:
            schema:
              properties:
                content: 
                  type: string
      responses:
        "200":
          description: Successful operation
          content:
            application/json:
              schema:
                properties:
                  content: 
                    type: string

上述接口的OpenAPI定义中,我们看到一个叫做operationId的字段,我们权且把它作为接口的唯一标识。

从上面的OpenAPI文档可知,它描述了调用接口所需的所有信息,所以我们可以写一个通用的方法来调用接口:

pom文件添加依赖:

xml 复制代码
    <dependency>
        <groupId>io.swagger.parser.v3</groupId>
        <artifactId>swagger-parser</artifactId>
        <version>2.1.16</version>
    </dependency>

接口实现原型大概如下所示:

java 复制代码
/**
 * 
 * @param openApiFile OpenAPI定义文件
 * @param operationId 接口的operationId
 * @param requestBody 请求body内容
 */
public String callOpenAPI(String openApiFile, String operationId, String requestBody) {
    // 解析OpenAPI文档为java内部表示:OpenAPI
    SwaggerParseResult parseResult = new OpenAPIV3Parser().readLocation(openApiFile, null, null);
    OpenAPI openAPI = parseResult.getOpenAPI();
    
    // 遍历文档中定义的所有path
    for (Map.Entry<String, PathItem> entry : openAPI.getPaths().entrySet()) {
        // 当前path
        String path = entry.getKey();
        PathItem pathItem = entry.getValue();
        // 当前path定义了Post方法
        if (pathItem.getPost() != null) {
            Operation postOperation = pathItem.getPost();
            // 当前方法的operationId,是我们的目标OperationId,也就是需要调用的方法
            String operationId = postOperation.getOperationId();
            if (Objects.equals(operationId, operationId)) {
                
                // 根据OpenAPI参数构造RestTemplate
                RestTemplate restTemplate = new RestTemplate();
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, headers);
                String response = restTemplate.postForObject("http://localhost:8102" + path, requestEntity, String.class);

                return response;
            }
        }
    }
    
    throw new IllegalArgumentException("not exist");
}
相关推荐
Wang's Blog35 分钟前
Java 项目实战: 交友项目 - 项目:需求分析与滑动数据设计
java·开发语言·交友
前端 贾公子1 小时前
第 5 章:Codex 从可运行程序到可交付工具
java·前端·数据库
CoderYanger1 小时前
Java EE 进阶:5. Spring IoC&DI(第三弹):Spring DI
java·开发语言·spring boot·spring·面试·java-ee·mvc
一条泥憨鱼1 小时前
【Kafka】零基础学习教程
java·分布式·学习·kafka
Wang's Blog2 小时前
Java 项目实战:交友项目 - 即时通信总结:全链路复盘、可复用清单与自检
java·python·交友
文丑颜不良啊2 小时前
LeetCode1501~1520题目解析
java·leetcode
长不大的杨瑞杰2 小时前
OpenCode v2 LSP 实战:内置语言服务器、自定义配置与诊断闭环
java·人工智能·typescript·lsp·opencode
云和数据.ChenGuang2 小时前
python和java的try
java·开发语言·python
日日大王学编程2 小时前
阿里巴巴开源 Data Agent(Text2SQL)项目全维度详解
java·ai·开源
Wang's Blog2 小时前
Java 项目实战:交友项目 - 交友左滑右滑
java·交友