中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

OpenAPI開發怎么動態的添加接口

發布時間:2023-04-13 17:20:03 來源:億速云 閱讀:177 作者:iii 欄目:開發技術

這篇文章主要介紹“OpenAPI開發怎么動態的添加接口”,在日常操作中,相信很多人在OpenAPI開發怎么動態的添加接口問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”OpenAPI開發怎么動態的添加接口”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

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 規范

OpenAPI開發怎么動態的添加接口

分為幾個組成部分:

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

到此,關于“OpenAPI開發怎么動態的添加接口”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

竹山县| 柏乡县| 手游| 伊春市| 忻州市| 汉沽区| 玉屏| 堆龙德庆县| 辛集市| 东安县| 安西县| 高密市| 沧源| 丰城市| 聂荣县| 新巴尔虎左旗| 镇原县| 德安县| 河池市| 茂名市| 远安县| 宣城市| 宜兴市| 高碑店市| 宜阳县| 即墨市| 宜春市| 集贤县| 北川| 丹巴县| 南丰县| 锡林郭勒盟| 红桥区| 湘乡市| 马边| 汉川市| 扎赉特旗| 万宁市| 稷山县| 安岳县| 天镇县|