最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解OpenAPI開發(fā)如何動態(tài)的添加接口實(shí)現(xiàn)

 更新時間:2023年04月13日 11:06:06   作者:爛筆頭  
這篇文章主要為大家介紹了OpenAPI開發(fā)如何動態(tài)的添加接口實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

0 | 需求說明

如何動態(tài)的處理接口的返回?cái)?shù)據(jù) 里提到了我們的業(yè)務(wù)場景:服務(wù)A對接了服務(wù)B,服務(wù)C等服務(wù)的一些接口,然后由服務(wù)A統(tǒng)一暴露接口給到外部用戶使用。

其中有個需求是:服務(wù)A可以動態(tài)的接入服務(wù)B/C的接口,對外暴露,并無需重啟服務(wù)A,即支持API接口的動態(tài)添加。

1 | 思路方案

傳統(tǒng)的API接口暴露方法

傳統(tǒng)的業(yè)務(wù)開發(fā),使用 springboot 的話,會把服務(wù)需要暴露的 API 接口寫在 controller 層里,然后調(diào)用 service 層的接口方法,在實(shí)現(xiàn)層 implement 該 service 接口方法的具體實(shí)現(xiàn)函數(shù)。

  • 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();
}
  • 實(shí)現(xiàn)層
@Service
public class HelloServiceImpl implements HelloService {
    @Override
    public String hello(){
        return "hello world";
    }
}

我們可以看到,在 controller 層 API 接口的 subpath 是寫好的,構(gòu)建部署之后,服務(wù)具有的 API 接口列表就固定了。如果需要新增 API 接口,就需要重新在 controller 里寫代碼,編譯構(gòu)建,再部署上線。這樣效率很低,而且每次部署,都會影響到線上服務(wù),也不安全。

泛化的方法

對于 OpenAPI 的業(yè)務(wù)場景來說,其實(shí)是不關(guān)心接入的 API subpath 具體是什么,只關(guān)心能不能通過 subpath 找到對應(yīng)的需要轉(zhuǎn)發(fā)的服務(wù)。

那么,在 controller 層,就可以不根據(jù)具體的 subpath 來匹配對應(yīng)的服務(wù),而是通過請求的方法get、post、put + 通配符的方式來分類接收外部請求,然后利用 AOP切面 和 Threadlocal 來處理和傳遞 subpath 攜帶的信息,給到實(shí)現(xiàn)層,最終在實(shí)現(xiàn)層分發(fā)請求到各個業(yè)務(wù)服務(wù)的 API 接口。

2 | 具體實(shí)施

OpenAPI 的 URL 規(guī)范

分為幾個組成部分:

  • http method: 請求方法,get、post、put、delete等
  • http scheme: http or https
  • OpenAPI統(tǒng)一域名: 外部訪問 OpenAPI 的統(tǒng)一域名
  • 資源訪問類型: 訪問的資源,api、web等
  • OpenAPI版本號: OpenAPI 服務(wù)自身的版本號
  • 內(nèi)部服務(wù)的名稱: OpenAPI 對接的內(nèi)部服務(wù)名稱(標(biāo)識)
  • 內(nèi)部服務(wù)的path: 對接內(nèi)部服務(wù)API的 subpath

代碼實(shí)現(xiàn)

泛化的 controller 層實(shí)現(xiàn) 以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實(shí)現(xiàn)層

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();
    }
}

至此,服務(wù)A可以動態(tài)的接入服務(wù)B/C的接口,對外暴露,并無需重啟服務(wù)A,即支持API接口的動態(tài)添加的業(yè)務(wù)需求實(shí)現(xiàn)完畢。

以上就是詳解OpenAPI開發(fā)如何動態(tài)的添加接口實(shí)現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于OpenAPI動態(tài)添加接口的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • tk.mybatis擴(kuò)展通用接口使用詳解

    tk.mybatis擴(kuò)展通用接口使用詳解

    這篇文章主要介紹了tk.mybatis擴(kuò)展通用接口使用詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-08-08
  • IDEA啟動Springboot報(bào)錯:無效的目標(biāo)發(fā)行版:17 的解決辦法

    IDEA啟動Springboot報(bào)錯:無效的目標(biāo)發(fā)行版:17 的解決辦法

    這篇文章主要給大家介紹了IDEA啟動Springboot報(bào)錯:無效的目標(biāo)發(fā)行版:17 的解決辦法,文中通過代碼示例和圖文講解的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-02-02
  • java實(shí)現(xiàn)遍歷樹形菜單兩種實(shí)現(xiàn)代碼分享

    java實(shí)現(xiàn)遍歷樹形菜單兩種實(shí)現(xiàn)代碼分享

    這篇文章主要介紹了java實(shí)現(xiàn)遍歷樹形菜單兩種實(shí)現(xiàn)代碼分享,兩種實(shí)現(xiàn):OpenSessionView實(shí)現(xiàn)、TreeAction實(shí)現(xiàn)。具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • 如何實(shí)現(xiàn)自己的spring boot starter

    如何實(shí)現(xiàn)自己的spring boot starter

    這篇文章主要介紹了如何實(shí)現(xiàn)自己的spring boot starter,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-08-08
  • 使用Maven打包、發(fā)布、配置版本號命令

    使用Maven打包、發(fā)布、配置版本號命令

    在軟件開發(fā)過程中,打包和發(fā)布是關(guān)鍵步驟,本文介紹了如何在打包和發(fā)布時跳過測試,如何指定項(xiàng)目版本號,以及如何指定配置文件,提供了實(shí)用的技巧和方法,希望對開發(fā)者有所幫助
    2024-09-09
  • Spring實(shí)戰(zhàn)之使用Resource作為屬性操作示例

    Spring實(shí)戰(zhàn)之使用Resource作為屬性操作示例

    這篇文章主要介紹了Spring實(shí)戰(zhàn)之使用Resource作為屬性,結(jié)合實(shí)例形式分析了spring載人Resource作為屬性相關(guān)配置與使用技巧,需要的朋友可以參考下
    2020-01-01
  • Java?輕松入門使用Fiddler抓包工具教程

    Java?輕松入門使用Fiddler抓包工具教程

    超文本傳輸協(xié)議(HTTP)是一個簡單的請求-響應(yīng)協(xié)議,其主要是基于TCP來實(shí)現(xiàn)的,可以通過Chrome開發(fā)者工具或者Wireshark或者Fiddler抓包,以便分析?HTTP?請求/響應(yīng)的細(xì)節(jié),本篇博客主要談?wù)撊绾问褂肍iddler抓取HTTP,當(dāng)然也可以抓取HTTPS
    2022-02-02
  • JavaEE中struts2實(shí)現(xiàn)文件上傳下載功能實(shí)例解析

    JavaEE中struts2實(shí)現(xiàn)文件上傳下載功能實(shí)例解析

    這篇文章主要為大家詳細(xì)介紹了JavaEE中struts2實(shí)現(xiàn)文件上傳下載功能實(shí)例,感興趣的小伙伴們可以參考一下
    2016-05-05
  • spring整合redis消息監(jiān)聽通知使用的實(shí)現(xiàn)示例

    spring整合redis消息監(jiān)聽通知使用的實(shí)現(xiàn)示例

    在電商系統(tǒng)中,秒殺,搶購,紅包優(yōu)惠卷等操作,一般都會設(shè)置時間限制,本文主要介紹了spring整合redis消息監(jiān)聽通知使用,具有一定的參考價值,感興趣的可以了解一下
    2021-12-12
  • Java @Value(

    Java @Value("${xxx}")取properties時中文亂碼的解決

    這篇文章主要介紹了Java @Value("${xxx}")取properties時中文亂碼的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07

最新評論

通城县| 五莲县| 平昌县| 邵武市| 河曲县| 崇仁县| 乃东县| 阜新| 定南县| 札达县| 会泽县| 井冈山市| 镇远县| 桐庐县| 沈阳市| 潢川县| 黄平县| 南溪县| 大关县| 板桥市| 古浪县| 略阳县| 临城县| 中宁县| 松桃| 阳原县| 承德县| 庄浪县| 东方市| 瑞安市| 永登县| 临邑县| 临海市| 镇雄县| 启东市| 石渠县| 五莲县| 贺兰县| 娄底市| 滦南县| 文成县|