辽阳专业网站开发公司,wordpress 如何使用html5游戏,百度风云榜排行榜,个人网页制作软件文章目录 1、需求背景2、接口抽象类具体实现类3、疑问4、存在的问题5、通过反射加载SDK并完成调用5、补充#xff1a;关于业务网关7、补充#xff1a;关于SDK的开发 关键点#xff1a;
接口抽象类#xff08;半抽象半实现#xff09;具体实现类业务网关反射加载SDK#… 文章目录 1、需求背景2、接口抽象类具体实现类3、疑问4、存在的问题5、通过反射加载SDK并完成调用5、补充关于业务网关7、补充关于SDK的开发 关键点
接口抽象类半抽象半实现具体实现类业务网关反射加载SDK完成统一调用 半路接手一个需求需要从自己系统出发经过业务网关的统一校验和转发来请求第三方供应商系统的接口整理下看同事代码学到的一点思路。
1、需求背景
第三方供应商需要上架自己的产品到公司的交易平台但用户使用产品时最后一步请求的自然是供应商自己的服务器资源和API。关于这个需求的实现思路大致是在交易平台需要做接口有效性校验、服务实例有效性校验等以及消费数据记录落库最后转发到供应商接口去请求资源既然是请求别人的系统那就涉及到怎么通过人家的鉴权系统。
PostMapping(/data/{platform}/{apiId})
public Object redirect(PathVariable String platform, PathVariable String apiId,RequestBody MapString, Object parameterMap, HttpServletRequest request) {//直接把API的ID放进请求参数里后面用完了再调三方API时去掉就行parameterMap.put(apiId, apiId);return redirectHandler.redirect(platform, ServletUtils.getHeaders(request), parameterMap);
}这里给访问所有三方系统接口一个统一的入口做为业务网关后面展开说接口传参中
paltform确定是哪个第三方系统apiID用来标识想请求第三方系统哪个的API接口通过这个ID可以在库里查到API的路径、三方系统的host、密钥、以及后面会提到的SDK的存储路径、SDK里的核心方法名等信息parameterMap用户传入的请求参数requestHttp请求对象
其中用工具类获取下HTTP请求的全部请求头信息存入Map。
public class ServletUtils{/*** 获取Http请求的请求头信息*/public static MapString, String getHeaders(HttpServletRequest request) {MapString, String map new LinkedHashMap();EnumerationString enumeration request.getHeaderNames();if (enumeration ! null) {while (enumeration.hasMoreElements()) {String key enumeration.nextElement();String value request.getHeader(key);map.put(key, value);}}return map;}}2、接口抽象类具体实现类
既然需要对接很多第三方供应商系统去调用第三方系统的API那就考虑定义一个接口里面抽象出一个做鉴权、转发的方法对接不同的供应商系统时去实现这个接口然后走不同的实现。
public interface ApiRedirectHandler {/*** param headerMap 请求头参数Map* param paramMap 对第三方接口的请求参数* return 返回第三方接用调用的结果*/Object redirect(MapString, String headerMap, MapString, Object paramMap);}前面提到在交易平台要做一些校验和消费记录落库的操作这些是对接所有三方系统的公共步骤而后面请求第三方系统接口肯定要做的鉴权认证以及转发或者调用则属于各个三方系统的定制化行为因为一个系统有一个系统的认证方式A系统用APP密钥、B系统可能用sign验签。因此考虑在接口下面垫一个抽象类抽象类中实现接口中的转发方法里面做校验、记录落库等操作同时调用本抽象类自己的抽象request方法这个方法里做第三方系统的定制化的认证和转发或调用。这样对接不同的三方系统只需就继承这个抽象类实现里面的request方法做自己的认证和转发即可。 总结全抽象的接口过渡到半抽象的抽象类抽象类中实现接口的抽象方法时方法体中写一部分公共逻辑 调用本抽象类自己的一个抽象方法B这个抽象方法B就给以后的普通类去继承和重写。 Slf4j
public abstract class AbstractRedirectHandler implements ApiRedirectHandler {//抽象类中实现接口的方法Overridepublic Object redirect(MapString, String headerMap, MapString, Object paramMap) {//todo: 1.请求有效性验证//从请求参数paramMap中拿到你要调用APIId然后查到的三方系统接口的路径、host等信息ApiInfo apiDetailVo queryApiInfo(paramMap);//API的ID用完了它不是三方系统接口需要的请求参数移除paramMap.remove(apiId); //todo: 2.服务实例有效性验证//request中去写不同三方系统的鉴权、转发或调用逻辑val responseData request(headerMap, paramMap, apiDetailVo);//todo: 3.记录消费记录//返回第三方接口的响应结果return responseData;}/*** API转发请求对接时针对不同的三方系统去定制化实现** param headerMap 头信息* param paramMap 请求参数* Param apiDetailVo 接口信息如接口路径、服务器host* return 返回第三方接用调用的结果*/protected abstract Object request(MapString, String headerMap, MapString, Object paramMap, ApiDetailVo apiDetailVo);}比如现在对接001号系统按它们系统支持的方式做认证比如header中添加APP密钥然后组装请求URL成一个HttpRequest对象发送Http请求即可完成对三方系统API的调用。
public class System001RedirectHandler extends AbstractRedirectHandler {Overridepublic Object request(MapString, String headerParam, MapString, Object paramMap, ApiDetailVo apiDetailVo) {//拿到三方系统的服务器HOST以及接口路径String url apiDetailVo.getHost() apiDetailVo.getPath();//拿到三方系统接口的请求方式POST还是GET...val method Method.valueOf(apiDetailVo.getRequestMethod());//使用Hutool工具类的HTTP请求对象方便后面调用现成的方法来发送HTTP请求HttpRequest request null;//如果是GETif (method.equals(Method.GET)) {String headerBody ;StringBuffer body new StringBuffer();StringBuffer param new StringBuffer();for (String key : paramMap.keySet()) {body.append(key).append().append(paramMap.get(key)).append();param.append(key).append().append(URLEncoder.encode((String) paramMap.get(key), StandardCharsets.UTF_8)).append();}//拼接出一个GET请求的完整路径if (param.length() 0) {headerBody body.substring(0, body.length() - 1);url url ? param.substring(0, param.length() - 1);}//创建request请求对象request HttpUtil.createGet(url);//请求头中加入001系统的认证秘钥以便通过认证request.addHeaders(getHeader(headerBody, apiDetailVo.getAppSecret()));} else {//POST请求request HttpUtil.createPost(url).contentType(application/json);String body JSON.toJSONString(paramMap);//组装请求头和请求体request.addHeaders(getHeader(body, apiDetailVo.getAppSecret())).body(body);}//库里存的API有要求超时时间if (apiDetailVo.getTimeout() 0) {request.timeout(apiDetailVo.getTimeout());}//发送HTTP请求拿到响应val httpResponse request.execute();return JSON.parseObject(httpResponse.body());}3、疑问
给所有三方系统接口的调用一个统一的请求入口怎么实现根据传入的第三方系统类型platformType来选择不同的实现类对象考虑把转发接口ApiRedirectHandler的所有实现类放进一个List遍历去匹配传入的platformType匹配则找到了三方系统对应的处理器实现类。找不到就给个默认的处理器实现类。
RequiredArgsConstructor
public class CompositeRedirectHandler {private ArrayListApiRedirectHandler handlers new ArrayList();public CompositeRedirectHandler(ArrayListApiRedirectHandler redirectHandlerList) {handlers redirectHandlerList;}public Object redirect(String platform, MapString, String headerMap, MapString, Object paramMap) {//给一个默认的通用执行器实现类对象ApiRedirectHandler execHandler handlers.get(0);//根据平台信息匹配到ApiRedirectHandler接口的三方系统的实现类for (ApiRedirectHandler handler : handlers) {if (handler.isMatched(platform)) {execHandler handler;break;}}//用实现类去调用转发方法 抽象类包含抽象方法request 各个三方系统对抽象类的实现 完成三方系统API的请求return execHandler.redirect(headerMap, paramMap);}}
上面的接口中注入这个CompositeRedirectHandler对象调用它的redirect方法即可全部串起来。
private final CompositeRedirectHandler redirectHandler;PostMapping(/data/{platform}/{apiId})
public Object redirect(PathVariable String platform, PathVariable String apiId,RequestBody MapString, Object parameterMap, HttpServletRequest request) {parameterMap.put(apiId, apiId);return redirectHandler.redirect(platform, ServletUtils.getHeaders(request), parameterMap);
}4、存在的问题
如此以后每对接一个三方系统就得开发一个新的实现类去按照他们系统支持的认证方式来做认证以及转发或调用。相当繁琐现在考虑把这个认证的事交给三方系统自己去完成比如让他们开发一个SDKSDK里他们按照自己系统支持的认证方式做能通过鉴权的操作到底是header里放密钥还是做验签我就不再关心了以及组装HTTP请求而我们只需要load这个SDK里的内容传入请求参数和路径做一个调用即可。 Slf4j
public class CommonRedirectHandler extends AbstractRedirectHandler {Overridepublic Object request(MapString, String headerMap, MapString, Object paramMap, ApiDetailVo apiDetailVo) {//根据apiDetailInfo加载对应的SDK完成调用//....如此我就只需要一个通用的实现类CommonRedirectHandler就可以实现对所有三方系统的对接这个通用类中也实现了上面的抽象类的request方法request方法中只需load SDK里三方系统开发者写的方法传入请求路径和请求参数即可完成三方系统接口的调用。
5、通过反射加载SDK并完成调用
现在问题成了如何加载SDK完成调用。 ⇒ 通过反射拿到核心类的对象以及负责认证和转发请求的核心方法最后完成调用即可。这里的反射直接用hutool这个强大的第三方依赖库。
!--引入hutool--
dependencygroupIdcn.hutool/groupIdartifactIdhutool-all/artifactIdversion5.3.9/version
/dependency加载SDK的示意代码
Slf4j
public class CommonRedirectHandler extends AbstractRedirectHandler {/*** 动态加载sdk调用里面已经完成鉴权和转发的方法以实现转发请求** param headerMap 头信息* param paramMap 请求参数* return 三方系统接口的返回数据*/Overridepublic Object request(MapString, String headerMap, MapString, Object paramMap, ApiDetailVo apiDetailVo) {//SDK的路径、类名、核心方法名val jarFilePath apiDetailVo.getSdkJarFilePath(); val classFullName apiDetailVo.getClassFullName();val invokeMethodName apiDetailVo.getInvokeMethodName();val httpMethod apiDetailVo.getRequestMethod().toUpperCase();//拼接完整的三方系统接口的URLString apiUrl apiDetailVo.getHost() apiDetailVo.getPath();log.info(file {}, new File(jarFilePath));//hutool工具类加载SDK成class对象Class? clazz ClassLoaderUtil.loadClass(new File(jarFilePath), classFullName);//反射拿到构造方法对象final val constructors ReflectUtil.getConstructor(clazz);Object instance null;try {//SDK核心类的对象instance constructors.newInstance();final val requestMethod ReflectUtil.getMethodByName(clazz, invokeMethodName);//调用return requestMethod.invoke(instance, apiUrl, httpMethod, headerMap, paramMap);} catch (InstantiationException e) {log.error(e.getMessage());throw new ServiceException(ExceptionCodeEnum.API_GATEWAY_REQUEST_API_ERROR);} catch (IllegalAccessException e) {log.error(e.getMessage());throw new ServiceException(ExceptionCodeEnum.API_GATEWAY_REQUEST_API_ERROR);} catch (InvocationTargetException e) {log.error(e.getCause().getMessage());throw new ServiceException(ExceptionCodeEnum.API_GATEWAY_REQUEST_API_ERROR);}}
}
5、补充关于业务网关
本需求里给请求第三方系统接口资源提供了一个统一的API入口比如
POSTMapping(/data/{platformType}/{API_ID})
public Object redirect(PathVariable String platformType, PathVariable String API_ID, RequestBody MapString, Object requestParam, HttpServletRequset requset){//.....
}有了这个统一入口请求三方系统资源就都从这个接口过前面说的各种合法性、有效性校验、记录落库、转发等就可以在这里完成了由此可见其虽然不比常规的Gateway服务比如SpringCloudGateway但干的活儿是类似的即校验和转发路由因此称业务网关。 思路给所有三方系统的api调用提供一个统一的入口(Api) 7、补充关于SDK的开发
SDKSoftware Development Kit即软件开发工具包。简单说就是造轮子实现一个小功能别人引入就能使用。往大了说如Java开发工具包JDK使用import引入相关的包
import java.util.*;往小了说如文件上传的SDK其他系统引入后就可用。关于SDK的开发需要注意
易用性提供统一调用用户不用关心内部实现的细节只需知道调谁、传什么、能返回什么即可
//常见方式1.直接调用
FileManage.upload(String filePath);//常见方式2.需要new对象
FileManage fileManage new FileManage();
fileManage.upload(String filePath);轻量依赖尽量减少SDK本身对其他类库的依赖以减少用户项目中的已有依赖和SDK依赖的冲突结构清晰如maven项目下service包下编写业务逻辑、constant包下存放常量、utils包下放工具类见名知意不用看说明文档也知道这个方法是干啥用的可扩展提供接口或者抽象类对外支持用户自己继承和按需写实现类如密码相关SDK做加密解密起名PasswordHandler其加密方法encode需要传入一个密码一个加密器这个加密器就可以提供成接口用户可通过实现这个接口来自定义加密方式。
//加密器对象按照非对称算法加密
Encoder encoder new SignEncoder();
String password PasswordHandler.encode(daihao9527, encoder);//用户自己实现加密器接口
public MyEncoder implements Encoder {Overridevoid doEncode(){//...用户自己写如采用时间戳、或自定义的MD5工具类Calendar calendar Calendar.getInstance();Long timestamp calendar.getTime.getTime();String sign MD5Utils.getMD5Str(timestamp secretKey);//..}
}//此时用户可以自己指定加密器
String password PasswordHandler.encode(daihao9527, new MyEncoder());SDK中的内容一般包括
功能模块实现功能APISDK的门面调用和使用功能的入口文档附相关使用说明和指引Demo使用示例运行Demo直观体验SDK功能