如何优雅的调用接口?

article/2025/11/4 20:52:58

目录

一、背景

二、分析

三、编码

InterfaceDescriptor

ServiceProviderInterface

ServiceProviderInterfaceClient

ServiceProviderInterfaceClientImpl

 四、测试

SendSmsRequest

SendSmsResult 

SendSmsProvider

TestController

五、总结


一、背景

一般我们的项目中,肯定少不了调第三方的http接口,比如说:调阿里云短信接口机发送短信,调邮件接口发送邮件,获取用户的某些数据,向第三方上报数据等等。

随着我们需要调的接口逐渐增多,代码将会越来越乱,不好好整理的话对于每个接口的维护成本将会增加。

下面我们就来优雅的去调用第三方接口,写出好维护、可扩展的代码。

二、分析

调用http接口一般就分为三步:

1、组装参数

2、按照接口 URl 和 调用方式,使用 httpClient 工具类去调用

3、处理响应返回

三、编码

InterfaceDescriptor

定义注解 InterfaceDescriptor,这里定义 接口地址 和 请求方式,是给业务调用方用的,用于标识这个业务需要调的接口地址和方式是什么

package com.maple.client.third;import java.lang.annotation.*;@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface InterfaceDescriptor {String interfaceUrl();String interfaceMethod();
}

ServiceProviderInterface

定义公共接口 ServiceProviderInterface,每次调用都需要组装请求和处理响应,所以我们这里定义公共方法,这里是给业务调用方实现的,因为每个业务所需要的 入参 和 响应 不同

package com.maple.client.third;import com.alibaba.fastjson.JSONObject;public interface ServiceProviderInterface<REQ, RES> {/*** 构建请求** @param request* @return*/JSONObject buildRequest(final REQ request);/*** 处理响应** @param jsonObject* @return*/RES processResponse(final JSONObject jsonObject);
}

ServiceProviderInterfaceClient

定义 ServiceProviderInterfaceClient,这个也是暴露给业务调用方的统一的接口,这个接口的请求参数是 上面的 ServiceProviderInterface 和 request

package com.maple.client.third;public interface ServiceProviderInterfaceClient {<REQ, RES> RES invoke(ServiceProviderInterface<REQ, RES> serviceProviderInterface, REQ request);
}

ServiceProviderInterfaceClientImpl

定义 ServiceProviderInterfaceClientImpl,是 ServiceProviderInterfaceClient 接口的实现类,这个实现类是我们具体的内部处理,而这个ServiceProviderInterface 接口的里包含了很多的数据可供我们获取到,实现类里的主要逻辑就是:

1、从注解里获取到 接口地址 和 请求方式

2、然后根据不同的请求方式去使用不同的httpClient工具类

3、通过 接口地址 和 请求参数 去做真实的调用,调用完成之后再去处理响应

package com.maple.client.third;import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Preconditions;
import org.springframework.stereotype.Component;@Component
public class ServiceProviderInterfaceClientImpl implements ServiceProviderInterfaceClient {@Overridepublic <REQ, RES> RES invoke(ServiceProviderInterface<REQ, RES> serviceProviderInterface, REQ request) {Preconditions.checkNotNull(serviceProviderInterface, "interface cannot be null");Preconditions.checkNotNull(request, "request cannot be null");String interfaceUrl, interfaceMethod;InterfaceDescriptor interfaceDescriptor = serviceProviderInterface.getClass().getAnnotation(InterfaceDescriptor.class);Preconditions.checkNotNull(interfaceDescriptor, "【interface】missing interface descriptor on ServiceProviderInterface class, class=" + getClass());interfaceUrl = interfaceDescriptor.interfaceUrl();interfaceMethod = interfaceDescriptor.interfaceMethod();RES result = null;try {result = doInvoke(interfaceUrl, interfaceMethod, serviceProviderInterface, request);return result;} catch (Exception e) {System.out.println("【interface】" + interfaceUrl + " invoke failed, error=" + e.getMessage());throw new RuntimeException("【interface】" + interfaceUrl + " 接口不可用");} finally {System.out.println("【interface】" + interfaceUrl + " invoke finished, result=" + result);}}private <RES, REQ> RES doInvoke(String interfaceUrl, String interfaceMethod, ServiceProviderInterface<REQ, RES> serviceProviderInterface, REQ request) {Preconditions.checkNotNull(interfaceUrl, "interface cannot be null");Preconditions.checkNotNull(interfaceMethod, "request cannot be null");//组装请求参数JSONObject requestJson = serviceProviderInterface.buildRequest(request);JSONObject result = new JSONObject();if ("GET".equalsIgnoreCase(interfaceMethod)) {System.out.println("GET方式调用http接口,入参为:" + requestJson);result.put("result", "GET方式调用http接口成功");} else if ("POST".equalsIgnoreCase(interfaceMethod)) {System.out.println("POST方式调用http接口,入参为:" + requestJson);result.put("result", "POST方式调用http接口成功");} else {throw new RuntimeException("【interface】" + interfaceUrl + ", " + interfaceMethod + "方式不支持http调用");}RES res = serviceProviderInterface.processResponse(result);return res;}
}

 四、测试

我们这里就以调接口发送短信为例

SendSmsRequest

定义入参 SendSmsRequest

package com.maple.client.third.request;import java.io.Serializable;public class SendSmsRequest implements Serializable {private String mobile;private String content;public String getMobile() {return mobile;}public void setMobile(String mobile) {this.mobile = mobile;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}@Overridepublic String toString() {return "SendSmsRequest{" +"mobile='" + mobile + '\'' +", content='" + content + '\'' +'}';}
}

SendSmsResult 

定义出参 SendSmsResult

package com.maple.client.third.result;import java.io.Serializable;public class SendSmsResult implements Serializable {private String status;public String getStatus() {return status;}public void setStatus(String status) {this.status = status;}@Overridepublic String toString() {return "SendSmsResult{" +"status='" + status + '\'' +'}';}
}

SendSmsProvider

定义发送短信核心类  SendSmsProvider

package com.maple.client.third.provider;import com.alibaba.fastjson.JSONObject;
import com.maple.client.third.InterfaceDescriptor;
import com.maple.client.third.ServiceProviderInterface;
import com.maple.client.third.request.SendSmsRequest;
import com.maple.client.third.result.SendSmsResult;
import org.springframework.stereotype.Component;@Component
@InterfaceDescriptor(interfaceUrl = "http://yunpian/send/sms", interfaceMethod = "POST")
public class SendSmsProvider implements ServiceProviderInterface<SendSmsRequest, SendSmsResult> {@Overridepublic JSONObject buildRequest(SendSmsRequest request) {JSONObject jsonObject = new JSONObject();jsonObject.put("mobile", request.getMobile());jsonObject.put("content", request.getContent());return jsonObject;}@Overridepublic SendSmsResult processResponse(JSONObject jsonObject) {SendSmsResult result = new SendSmsResult();String status = jsonObject.getString("status");result.setStatus(status);return result;}
}

TestController

定义 测试类 TestController

package com.maple.client.controller;import com.maple.client.third.ServiceProviderInterfaceClient;
import com.maple.client.third.provider.SendSmsProvider;
import com.maple.client.third.request.SendSmsRequest;
import com.maple.client.third.result.SendSmsResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestController
public class TestController {@Resourceprivate ServiceProviderInterfaceClient serviceProviderInterfaceClient;@Resourceprivate SendSmsProvider sendSmsProvider;@GetMapping("/sendSms")public SendSmsResult sendSms() {SendSmsRequest sendSmsRequest = new SendSmsRequest();sendSmsRequest.setMobile("123");sendSmsRequest.setContent("写代码");SendSmsResult result = serviceProviderInterfaceClient.invoke(sendSmsProvider, sendSmsRequest);return result;}
}

五、总结

通过以上的编码,我们的思路就很清晰了,接入方只需要实现 ServiceProviderInterfaceClient 接口,不需要关注具体调用的细节方面;

只需要编写类似 SendSmsProvider的类 ,关注于自己的业务(接口地址,调用方式,接口入参,接口响应)即可,思路更加的清晰,代码更好的维护和扩展。

 


http://chatgpt.dhexx.cn/article/mM1CR5Jk.shtml

相关文章

软件测试 (7) web项目测试

前言 之前说了APP项目的测试&#xff0c;今天来总结一下web项目的测试&#xff0c;其实大多数过程是差不多类似的。对比平常移动端手机的高频率使用来说&#xff0c;对于部分人使用pc端浏览器的概率就不会这么高&#xff0c;讲APP项目测试的时候没怎么涉及到网络协议部分的介绍…

项目流程_软件测试

软件测试属性 1.按测试阶段划分 单元测试&#xff1a;单元测试是对软件基本组成单元进行的测试&#xff0c;是为了尽早发现错误(错误发现越早,成本越低&#xff0c;发现问题比较容易&#xff0c;修正问题更容易)&#xff0c;单个的软件单元或者一组相关的软件单元所进行的测试&…

[SpringBoot系列]项目测试

文章目录 加载测试专用属性加载测试专用配置Web环境模拟测试数据层测试回滚测试用例数据设定 加载测试专用属性 测试是保障程序正确性的唯一屏障&#xff0c;在企业级开发中更是不可缺少。 测试过程本身并不是一个复杂的过程&#xff0c;但是很多情况下测试时需要模拟一些线上情…

如何找软件测试的项目

10年测试经验分享&#xff1a;新手怎么找软件测试的项目&#xff1f;_程序员二黑的博客-CSDN博客_软件测试项目去哪里找测试新手不知道上哪找测试项目&#xff0c;这应该是每个测试自学人的困扰。https://erhei.blog.csdn.net/article/details/119416292?spm1001.2014.3001.55…

IT项目管理之软件测试

1. 定义 软件测试是使用人工或者自动的手段来运行或者测定某个软件系统的过程&#xff0c;其目的在于检验它是否满足规定的需求或弄清预期结果与实际结果之间的差别。 在软件投入使用前&#xff0c;要经过一系列的严格测试&#xff0c;才能保证交付质量。 2. QC & QA &a…

软件项目测试的具体内容

功能测试&#xff1a; 对指定业务所有功能进行测试&#xff0c;撰写测试用例&#xff0c;执行测试用例&#xff0c;出具测试报告和BUG列表&#xff0c;专用BUG管理工具进行测试管理。 根据产品特性、操作描述和用户方案&#xff0c;测试一个产品的特性和可操作行为以确定它们…

Linux下轻量级数据库-SQLite3(嵌入式设备)

一、概念 数据库是用来存储和管理数据的专用软件&#xff0c;使得管理数据更加安全&#xff0c;方便和高效。数据库对数据的管理的基本单位是表(table)。 二、常见的数据库 大型数据库(大型机) —————- Oracle(亿级) 中型数据库(分布式超大型) ———— mysql(百万级) 轻…

二、MySQL

MySQL是一个小型关系数据库管理系统。与其他大型数据库管理系统 &#xff08;例如Oracle、DB2、SQL Server 等&#xff09;相比&#xff0c;MySQL规模小、功能有限&#xff0c;但是它体积小、速度快、成本低&#xff0c;并且提供的功能对稍微复杂的应用来说已经够用&#xff0c…

数据库——多种方法导入Excel数据

文章目录 一、SQL Server导入Excel数据二、小技巧导入Excel数据三、使用Microsoft.ACE.OLEDB导入Excel数据四、手动添加一行数据五、手动添加多行数据五、解决类型不匹配致导入失败 一、SQL Server导入Excel数据 接下来就一直点击NEXT&#xff0c;直到完成 此时EXCEL的数据就被…

MySQL数据库简介

MySQL数据库简介 MySQL数据库是一种关系型数据库管理系统&#xff0c;是一种开源软件由瑞典MySQL AB公司开发&#xff0c;08年1月16日被Sun公司收购&#xff0c;09年Sun公司又被Oracle公司收购。 由于其体积小、速度快、总体拥有成本低&#xff0c;尤其是开放源码这一特点&a…

Linux中的小型数据库 SQLite3

数据库&#xff08;SQLite&#xff09; 数据库后缀 .db&#xff08;data base) gcc时加 -lsqlite3 这些是小希归纳的SQLite的一些重要语句&#xff0c;与示例应用 如果想要进一步学习&#xff0c;大家可以去网上搜 SQLite3的书籍

在JSTL 中使用小型数据库 SQLite

文章目录 一、Sqlite 数据库命令行工具的下载下载地址&#xff1a; 二、Sqlite 数据库 shell 工具的使用方法1.创建数据库2.Sqlite 数据库中表的创建&#xff1a;3.表中数据的插入4.Sqlite 数据库命令行的退出 三、ER图转化为数据库3.1建表3.2 向表中插入数据3.3 输出数据库中相…

实现一个小型数据库--记一次中级软件设计实作浮沉历程

说在前头&#xff1a;本篇文章主要是记录这次项目的过程&#xff0c;不全是贴代码&#xff0c;具体的程序移步这里&#xff0c;不喜欢的同学请轻喷。 事件起因&#xff1a;大三狗一枚&#xff0c;专业是软件应用。这学期的中级软件设计实作题目是实现一个小型的数据库&#xf…

用xml充当小型数据库案例

利用dom4j工具&#xff0c;将user.xml作为一个小型的数据库&#xff0c;然后控制台输入进行用户名与密码的匹配 /*user.xml文件放在src下*/ <?xml version"1.0" encoding"utf-8"?> <users><user id"001" name"ecri" …

数据库上机3(小型数据库应用程序开发)

注&#xff1a; ①各上机报告均根据《数据库技术与应用》课程的上机任务所做。 ②课程教材为 《数据库系统概论&#xff08;第五版&#xff09;》/王珊, 萨师煊编著/北京:高等教育出版社,2014 上机要求&#xff1a; 1、自学上层应用访问数据库的方式&#xff08;如ODBC、ADO、…

【SQL数据库设计】数据库设计【小型数据库】

数据库设计 需求 表结构字段类型、是否允许为null、是否有默认值索引设计数据库引擎的选择 根据产品原型分析&#xff0c;词性分析法&#xff0c;名词创建表或字段&#xff0c;动词表示关系。 数据存储&#xff1a;长期存储的数据&#xff0c; 1.主键&#xff1a;唯一、自增。 …

小型数据库系统开发作业

文章目录 题目数据库设计UI界面开发展示 原文链接&#xff1a;https://zhanghan.xyz/posts/60088/ 题目 自学上层应用访问数据库的方式&#xff08;如ODBC、ADO、JDBC、MySQLi或者其它&#xff09;&#xff0c;根据您使用的上层语言&#xff08;不限语言&#xff08;但要求与自…

关于Freesurfer提取annotation分区结构特征的命令mri_segstats

Freesurfer提供了基于分区模板提取常规特征&#xff08;area, volume, thickness, thicknessstd, meancurv, gauscurv, foldind, curvind&#xff09;的指令mris_anatomical_statsaparcstats2table还有可以提取从fMRI或DWI产生的结果以及一些特殊的结构特征&#xff08;如LGI&a…

Linux下使用Freesurfer的两种方法

文章目录 1. 直接在终端使用准备工作开始运行 2. 通过调用python文件使用 1. 直接在终端使用 重点&#xff1a;Freesurfer安装完成后&#xff0c;需要在相同的用户名下运行&#xff0c;并且所有文件数据也要放在此用户下 放在其他地方可能会因为没有权限而运行失败 官网流程说…

freesurfer入门-试图理解freesurfer输出的数据

因为一些原因想要学习使用freesurfer,虽然照着tutorial跑了起来但是完全不知道跑出来的是个啥,所以准备弄一篇文章记录每个部分是干啥的,此文为记录 能记多少是多少吧… FreeSurfer内数据 labelmriorgtransforms scriptsstatssurf 首先要提一下比较常见的两个词:lh和rh 如果我没…