iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >详解OpenAPI开发如何动态的添加接口实现
  • 950
分享到

详解OpenAPI开发如何动态的添加接口实现

OpenAPI动态添加接口OpenAPI接口添加 2023-05-15 17:05:25 950人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录0 | 需求说明1 | 思路方案传统的api接口暴露方法泛化的方法2 | 具体实施OpenAPI 的 URL 规范代码实现0 | 需求说明 在如何动态的处理接口的返回数据 里提

0 | 需求说明

在如何动态的处理接口的返回数据 里提到了我们的业务场景:服务A对接了服务B,服务C等服务的一些接口,然后由服务A统一暴露接口给到外部用户使用。

其中有个需求是:服务A可以动态的接入服务B/C的接口,对外暴露,并无需重启服务A,即支持API接口的动态添加。

1 | 思路方案

传统的API接口暴露方法

传统的业务开发,使用 SpringBoot 的话,会把服务需要暴露的 API 接口写在 controller 层里,然后调用 service 层的接口方法,在实现层 implement 该 service 接口方法的具体实现函数。

  • controller层
@RestController
@RequestMapping({"/v1"})
@Slf4j
public class HelloController {
    @Autowired
    private HelloService helloService;
    @PostMapping(path = {"/hello"})
    public String hello() {
        return Optional.ofNullable(helloService.hello())
                .map(ret -> new ResponseEntity<>(ret, httpstatus.OK))
                .orElseThrow(() -> new MMException("something wrong"));
    }
}
  • service层
public interface HelloService {
    String hello();
}
  • 实现层

@Service
public class HelloServiceImpl implements HelloService {
    @Override
    public String hello(){
        return "hello world";
    }
}

我们可以看到,在 controller 层 API 接口的 subpath 是写好的,构建部署之后,服务具有的 API 接口列表就固定了。如果需要新增 API 接口,就需要重新在 controller 里写代码,编译构建,再部署上线。这样效率很低,而且每次部署,都会影响到线上服务,也不安全

泛化的方法

对于 OpenAPI 的业务场景来说,其实是不关心接入的 API subpath 具体是什么,只关心能不能通过 subpath 找到对应的需要转发的服务。

那么,在 controller 层,就可以不根据具体的 subpath 来匹配对应的服务,而是通过请求的方法get、post、put + 通配符的方式来分类接收外部请求,然后利用 aop切面 和 Threadlocal 来处理和传递 subpath 携带的信息,给到实现层,最终在实现层分发请求到各个业务服务的 API 接口。

2 | 具体实施

OpenAPI 的 URL 规范

分为几个组成部分:

  • Http method: 请求方法,get、post、put、delete等
  • http scheme: http or https
  • OpenAPI统一域名: 外部访问 OpenAPI 的统一域名
  • 资源访问类型: 访问的资源,api、WEB
  • OpenAPI版本号: OpenAPI 服务自身的版本号
  • 内部服务的名称: OpenAPI 对接的内部服务名称(标识)
  • 内部服务的path: 对接内部服务API的 subpath

代码实现

泛化的 controller 层实现 以Get、Post请求为例:

@RestController
@RequestMapping({"/v1"})
@Slf4j
@ServicePath
public class OpenApiController {
    @Autowired
    private OpenApiService openApiService;
    @ApiOperation("OpenAPI POST接收")
    @PostMapping(path = {"/**"})
    public ResponseEntity<ReturnBase> filterPost(@Validated @RequestBody(required = false) Map<String, Object> reqMap) {
        return Optional.ofNullable(openApiService.filter(reqMap, null))
                .map(ret -> new ResponseEntity<>(ret, HttpStatus.OK))
                .orElseThrow(() -> new MMException("error.openapi.filter", ReturnEnum.C_GENERAL_BUSINESS_ERROR.getMsGCode()));
    }
    @ApiOperation("OpenAPI GET接收")
    @GetMapping(path = {"/**"})
    public ResponseEntity<ReturnBase> filterGet(@RequestParam(required = false) MultiValueMap<String, String> params) {
        return Optional.ofNullable(openApiService.filter(null, params))
                .map(ret -> new ResponseEntity<>(ret, HttpStatus.OK))
                .orElseThrow(() -> new MMException("error.openapi.filter", ReturnEnum.C_GENERAL_BUSINESS_ERROR.getMsgCode()));
    }
}

service层和service实现层

public interface OpenApiService {
    ReturnBase filter(Map<String, Object> reqBodyMap, MultiValueMap<String, String> reqGetParamsMap);
}
@Service
@Slf4j
public class OpenApiServiceImpl implements OpenApiService {
    @Override
    public ReturnBase filter(Map<String, Object> reqBodyMap, MultiValueMap<String, String> reqGetParamsMap) {
        String svcName = (String) OpenapiThreadlocal.getServiceParams().get(BizConstant.SVC_NAME);
        String svcPathPublic = (String) OpenapiThreadlocal.getServiceParams().get(BizConstant.SVC_PATH_PUBLIC);
        return doBizHandler(svcName, svcPathPublic);
    }
}

AOP切面和注解

@Aspect
@Component
@Slf4j
@Order(1)
public class ServicePathAspect {
    static final Pattern PATTERN = Pattern.compile("/v\\d+(/.+)");
    @Resource
    private CustomProperty customProperty;
    @Pointcut("@within(org.xxx.annotation.ServicePath)")
    public void servicePathOnClass() {}
    @Pointcut("@annotation(org.xxx.annotation.ServicePath)")
    public void servicePathOnMethod() {
    }
    @Before(value = "servicePathOnClass() || servicePathOnMethod()")
    public void before() {
        HttpServletRequest hsr = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String reqUri = hsr.getRequestURI();
        String httpMethod = hsr.getMethod();
        if (StrUtil.isEmpty(reqUri)) {
            log.error("request uri is empty");
            throw new MMException(ReturnEnum.A_PARAM_VALIDATION_ERROR);
        }
        Matcher matcher = PATTERN.matcher(reqUri);
        String servicePath = "";
        while (matcher.find()) {
            servicePath = matcher.group(1);
        }
        if (StrUtil.isEmpty(servicePath)) {
            log.error("can't parse service path from {}", reqUri);
            throw new MMException(ReturnEnum.A_PARAM_VALIDATION_ERROR);
        }
        String[] split = servicePath.split("\\/");
        if (split.length < 3) {
            log.error("api fORMat error: {}", servicePath);
            throw new MMException(ReturnEnum.A_PARAM_VALIDATION_ERROR);
        }
        String serviceName = split[1];
        servicePath = servicePath.substring(serviceName.length() + 1);
        Map<String, Object> map = Maps.newHashMap();
        map.put(BizConstant.SVC_NAME, serviceName);
        map.put(BizConstant.SVC_PATH_PUBLIC, servicePath);
        map.put(BizConstant.START_TIMESTAMP, start);
        OpenapiThreadlocal.addServiceParams(map);
    }
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ServicePath {
}

Threadlocal工具

public class OpenapiThreadlocal {
    private final static ThreadLocal<Map<String,Object>> SERVICE_PARAMS_HOLDER = new ThreadLocal<>();
    public static void addServiceParams(Map<String, Object> svcParamsMap) {
        SERVICE_PARAMS_HOLDER.set(svcParamsMap);
    }
    public static Map<String, Object> getServiceParams() {
        return SERVICE_PARAMS_HOLDER.get();
    }
    public static void removeServiceParams() {
        SERVICE_PARAMS_HOLDER.remove();
    }
}

至此,服务A可以动态的接入服务B/C的接口,对外暴露,并无需重启服务A,即支持API接口的动态添加的业务需求实现完毕。

以上就是详解OpenAPI开发如何动态的添加接口实现的详细内容,更多关于OpenAPI动态添加接口的资料请关注编程网其它相关文章!

--结束END--

本文标题: 详解OpenAPI开发如何动态的添加接口实现

本文链接: https://www.lsjlt.com/news/209302.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 详解OpenAPI开发如何动态的添加接口实现
    目录0 | 需求说明1 | 思路方案传统的API接口暴露方法泛化的方法2 | 具体实施OpenAPI 的 URL 规范代码实现0 | 需求说明 在如何动态的处理接口的返回数据 里提...
    99+
    2023-05-15
    OpenAPI动态添加接口 OpenAPI接口添加
  • OpenAPI开发怎么动态的添加接口
    这篇文章主要介绍“OpenAPI开发怎么动态的添加接口”,在日常操作中,相信很多人在OpenAPI开发怎么动态的添加接口问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”OpenAPI开发怎么动态的添加接口”的疑...
    99+
    2023-07-06
  • 详解如何实现OpenAPI开发动态处理接口的返回数据
    目录0 | 需求说明1 | 思路方案2 | 具体实施0 | 需求说明 业务场景:服务A对接了服务B,服务C等服务的一些接口,然后由服务A统一暴露接口给到外部用户使用。 需求是: 服...
    99+
    2023-05-15
    OpenAPI动态处理接口返回数据 OpenAPI 接口返回处理
  • 怎么实现OpenAPI开发动态处理接口的返回数据
    这篇文章主要介绍“怎么实现OpenAPI开发动态处理接口的返回数据”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“怎么实现OpenAPI开发动态处理接口的返回数据”文章能帮助大家解决问题。0 | 需求...
    99+
    2023-07-06
  • 详解Spring中实现接口动态的解决方法
    前言本文主要给大家介绍的是关于Spring实现接口动态的相关内容,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍吧。关于这个问题是因为领导最近跟我提了一个需求,是有关于实现类Mybatis的@Select、@Insert注解的功...
    99+
    2023-05-31
    spring 动态接口
  • 详解如何在Vue中动态添加类名
    目录静态和动态类有条件的类名使用数组语法使用对象语法与自定义组件一起使用快速生成类名使用计算属性来简化类能够向组件添加动态类名是非常强大的功能。它使我们可以更轻松地编写自定义主题,根...
    99+
    2024-04-02
  • Android ListView中动态添加RaidoButton的实例详解
    Android ListView中动态添加RaidoButton的实例详解这里讲解的内容是:从数据库中取得数据,将这些数据的value值赋值给Radiobutton的text属性,将这些数据的key值赋值给radiobutton的key值。...
    99+
    2023-05-30
    android listview roi
  • Android开发实现的ViewPager引导页功能(动态加载指示器)详解
    本文实例讲述了Android开发实现的ViewPager引导页功能(动态加载指示器)。分享给大家供大家参考,具体如下:先看效果图咯~现在几乎每个App都会有引导页,是不是感觉很炫很厉害,所以就想做出来一个学习一下~让自己的App看起来更加的...
    99+
    2023-05-30
  • Java为实体类动态添加属性的方法详解
    目录添加依赖代码测试可以给已有实体类动态的添加字段并返回新的实体对象,不影响原来的实体对象结构。 添加依赖 <dependency> ...
    99+
    2024-04-02
  • 如何通过php接口和ECharts实现统计图的数据动态加载
    如何通过PHP接口和ECharts实现统计图的数据动态加载【引言】随着数据可视化越来越受到企业和开发者的重视,统计图的应用越来越广泛。ECharts作为一款开源的JavaScript图表库,提供了丰富的图表类型和交互手段,结合PHP接口,可...
    99+
    2023-12-17
    echarts PHP接口 数据动态加载
  • vue动态添加表单validateField验证功能如何实现
    今天小编给大家分享一下vue动态添加表单validateField验证功能如何实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下...
    99+
    2023-07-05
  • 如何用Vue.js实现动态添加、删除选题功能
    这篇“如何用Vue.js实现动态添加、删除选题功能”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“如何用Vue.js实现动态添...
    99+
    2023-07-04
  • Android实现动态添加数据与堆叠折线图详解流程
    目录效果视频引用描述导包代码分析初始化动态添加数据温度数据湿度数据光照数据动态添加X轴时间值初始化自动刷新时间实现尾言效果视频 引用 描述 本示例采用的是非常、非常、非常好用的一款...
    99+
    2024-04-02
  • C++详解如何实现动态数组
    目录动态数组示例代码运行环境运行效果动态数组 动态数组Vector可以动态扩展内存,其采用连续的内存空间,当内存空间不足,便以原来的容量的2倍或者1.5倍成倍的扩展,将原有的数组元素...
    99+
    2024-04-02
  • jQuery如何实现的简单动态添加、删除表格功能
    这篇文章将为大家详细讲解有关jQuery如何实现的简单动态添加、删除表格功能,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。先来看看运行效果:具体代码如下:<!DOC...
    99+
    2024-04-02
  • elementUI如何动态给el-tree添加子节点数据children详解
    目录一、需求二、实现1. tree 的实例事件 node-click2. tree 的实例方法:updateKeyChildren3. 自动展示当前被点击的节点4. 页面重...
    99+
    2022-11-16
    element tree 添加子节点 element el-tree elementui el-tree
  • MyBatis详解如何实现Dao层接口
    目录传统开发方式编写UserDao接口编写UserDaompl实现传统测试方法代理开发方法代理开发方式介绍编写UserMapper接口测试代理方法传统开发方式 编写UserDao接口...
    99+
    2024-04-02
  • springboot 实现接口灰度发布的实例详解
    目录前言最小化改造方式springmvc接口请求原理HandlerMapping简介RequestCondition接口定义代码实现过程1、添加一个自定义注解用于标注接口类以及接口方...
    99+
    2024-04-02
  • Java中反射动态代理接口的详解及实例
    Java语言中反射动态代理接口的解释与演示Java在JDK1.3的时候引入了动态代理机制、可以运用在框架编程与平台编程时候捕获事件、审核数据、日志等功能实现,首先看一下设计模式的UML图解:当你调用一个接口API时候,实际实现类继承该接口,...
    99+
    2023-05-31
    java 反射 动态代理
  • 如何使用spring动态获取接口的不同实现类
    这篇“如何使用spring动态获取接口的不同实现类”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“如何使用spring动态获取...
    99+
    2023-06-29
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作