前面我们基于netty组件实现了消息服务,接下来,我们开始说一说API服务
先来回顾下API服务的整体设计。
API服务是通用接口平台的主体部分,对外暴露Restful风格的数据接口,其他应用系统通过调用API服务,一方面,可以查询权限范围内的业务数据,另一方面,可以将自身系统产生的数据推送至接口平台。
API服务分为服务技术框架和具体业务功能接口两部分,技术框架部分负责统一调度、数据验证、身份认证、安全控制、日志记录等职责,具体业务功能接口负责实际的业务接口功能处理,总体处理流程如下图所示:
基于该总体设计,我们可以简单粗暴,用1个controller来解决
/**
* api服务 控制器
*
* @author wqliu
* @date 2021-8-19 13:56
**/
@RestController
@RequestMapping("/api")
@Slf4j
public class ApiRestController {
/**
* 有效时间
*/
private static final int VALID_TIME_SPAN = 10;
@Autowired
private ApiServiceService apiServiceService;
@Autowired
private ApiAppService apiAppService;
@Autowired
private ApiServicePermissionService apiServicePermissionService;
@Autowired
private ApiServiceLogService apiServiceLogService;
@PostMapping("/rest")
@AllowAll
public ResponseEntity<ApiResponse> post(@Validated @RequestBody ApiRequest apiRequest) {
//记录收到请求时间
LocalDateTime receiveTime = LocalDateTime.now();
//定义返回
ApiResponse apiResponse = new ApiResponse();
try {
//验证数据
//TODO:方便测试,临时取消验证
// validateData(apiRequest);
//分发服务
ServiceHandler handler = dispatchService(apiRequest);
//业务处理
String responseData = handler.handle(apiRequest.getData());
//执行成功
apiResponse.setData(responseData);
apiResponse.setExecuteResult(ApiServiceExecuteResultEnum.SUCCESS.name());
} catch (CustomException ex) {
//自定义异常处理
apiResponse.setExecuteResult(ApiServiceExecuteResultEnum.ERROR.name());
apiResponse.setErrorCode("S00");
apiResponse.setErrorMessage(ex.getMessage());
} catch (ApiException ex) {
//自定义异常处理
apiResponse.setExecuteResult(ApiServiceExecuteResultEnum.ERROR.name());
apiResponse.setErrorCode(ex.getErrorCode());
apiResponse.setErrorMessage(ex.getMessage());
} catch (Exception ex) {
//非预期异常处理
apiResponse.setExecuteResult(ApiServiceExecuteResultEnum.ERROR.name());
apiResponse.setErrorCode("S99");
apiResponse.setErrorMessage("未定义异常:" + ex.getMessage());
} finally {
//记录请求响应时间
LocalDateTime responseTime = LocalDateTime.now();
//记录日志
recordLog(apiRequest, apiResponse, receiveTime, responseTime);
}
return new ResponseEntity<ApiResponse>(apiResponse, HttpStatus.OK);
}
/**
* 记录日志
*
* @param apiRequest
* @param apiResponse
* @param receiveTime
*/
private void recordLog(ApiRequest apiRequest, ApiResponse apiResponse, LocalDateTime receiveTime, LocalDateTime responseTime) {
ApiServiceLog apiServiceLog = new ApiServiceLog();
apiServiceLog.setAppCode(apiRequest.getAppCode());
apiServiceLog.setServiceCode(apiRequest.getServiceCode());
apiServiceLog.setRequestTime(DateUtils.toLocalDateTime(apiRequest.getRequestTime()));
apiServiceLog.setReceiveTime(receiveTime);
apiServiceLog.setRequestBusinessData(apiRequest.getData());
apiServiceLog.setExecuteResult(apiResponse.getExecuteResult());
apiServiceLog.setErrorCode(apiResponse.getErrorCode());
apiServiceLog.setErrorMessage(apiResponse.getErrorMessage());
apiServiceLog.setResponseTime(responseTime);
apiServiceLog.setResponseBusinessData(apiResponse.getData());
//计算执行时间
apiServiceLog.setExecutionTime(Duration.between(receiveTime, responseTime).toMillis());
//保存
apiServiceLogService.add(apiServiceLog);
}
/**
* 根据服务代码进行服务分发
*
* @param apiRequest
* @return
*/
private ServiceHandler dispatchService(ApiRequest apiRequest) {
String serviceCode = apiRequest.getServiceCode();
String handlerClass = apiServiceService.lambdaQuery().eq(ApiService::getCode, serviceCode)
.list().get(0).getHandler();
try {
ServiceHandler handler = (ServiceHandler) Class.forName(handlerClass).newInstance();
return handler;
} catch (Exception e) {
throw new ApiException("S60", "未找到该服务对应的处理器");
}
}
//region 数据验证
/**
* 数据验证
*
* @param apiRequest
*/
private void validateData(ApiRequest apiRequest) {
//验证参数
validateParameter(apiRequest);
//验证应用
validateApp(apiRequest);
//验证服务
validateService(apiRequest);
//验证权限
validatePermission(apiRequest);
//验证时效
validateTimeLimitation(apiRequest);
//验证签名
validateSign(apiRequest);
}
/**
* 验证参数
*
* @param apiRequest api请求
*/
private void validateParameter(ApiRequest apiRequest) {
//参数验证
//验证请求时间
String requestTime = apiRequest.getRequestTime();
Calendar validate = TimeValidator.getInstance().validate(requestTime, DateConstant.DATE_FORMAT_FULL);
if (validate == null) {
throw new ApiException("S01", "请求时间格式不符合要求");
}
}
/**
* 验证应用
*
* @param apiRequest api请求
*/
private void validateApp(ApiRequest apiRequest) {
//获取应用代码
String appCode = apiRequest.getAppCode();
//验证
List<ApiApp> list = apiAppService.lambdaQuery().eq(ApiApp::getCode, appCode).list();
if (CollectionUtils.isEmpty(list)) {
throw new ApiException("S10", "应用代码不存在");
}
if (list.get(0).getStatus().equals(StatusEnum.DEAD.name())) {
throw new ApiException("S11", "应用已停用");
}
}
/**
* 验证服务
*
* @param apiRequest api请求
*/
private void validateService(ApiRequest apiRequest) {
//获取服务代码
String serviceCode = apiRequest.getServiceCode();
//验证
List<ApiService> list = apiServiceService.lambdaQuery().eq(ApiService::getCode, serviceCode).list();
if (CollectionUtils.isEmpty(list)) {
throw new ApiException("S20", "服务不存在");
}
if (list.get(0).getStatus().equals(StatusEnum.DEAD.name())) {
throw new ApiException("S21", "服务已停用");
}
}
/**
* 验证权限
*
* @param apiRequest api请求
*/
private void validatePermission(ApiRequest apiRequest) {
//获取应用标识
String appCode = apiRequest.getAppCode();
String appId = apiAppService.lambdaQuery().eq(ApiApp::getCode, appCode).list().get(0).getId();
//获取服务标识
String serviceCode = apiRequest.getServiceCode();
String serviceId = apiServiceService.lambdaQuery().eq(ApiService::getCode, serviceCode).list().get(0).getId();
//验证
List<ApiServicePermission> list = apiServicePermissionService.lambdaQuery()
.eq(ApiServicePermission::getAppId, appId)
.eq(ApiServicePermission::getServiceId, serviceId)
.list();
if (CollectionUtils.isEmpty(list)) {
throw new ApiException("S30", "无权限");
}
}
/**
* 验证时效
*
* @param apiRequest api请求
*/
private void validateTimeLimitation(ApiRequest apiRequest) {
//获取请求时间
String requestTimeString = apiRequest.getRequestTime();
//转换时间格式
LocalDateTime requestTime = DateUtils.toLocalDateTime(requestTimeString);
//获取当前时间
LocalDateTime now = LocalDateTime.now();
//获取时间差值
Duration duration = Duration.between(requestTime, now);
if (Math.abs(duration.toMinutes()) > VALID_TIME_SPAN) {
throw new ApiException("S40", "请求时间超出合理范围");
}
}
/**
* 验证签名
*
* @param apiRequest api请求
*/
private void validateSign(ApiRequest apiRequest) {
//参数进入map,并自动排序
TreeMap<String, String> requestDataMap = new TreeMap<>();
requestDataMap.put("appCode", apiRequest.getAppCode());
requestDataMap.put("data", apiRequest.getData());
requestDataMap.put("requestTime", apiRequest.getRequestTime());
requestDataMap.put("serviceCode", apiRequest.getServiceCode());
requestDataMap.put("signMethod", apiRequest.getSignMethod());
//获取密钥
String appSecret = apiAppService.lambdaQuery().eq(ApiApp::getCode, apiRequest.getAppCode()).list().get(0).getSecret();
//拼装参数与密钥
StringBuffer sb = new StringBuffer();
sb.append(appSecret);
Iterator<String> iterator = requestDataMap.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
sb.append(key).append("=").append(requestDataMap.get(key));
}
sb.append(appSecret);
String param = sb.toString();
// 进行签名
String sign = DigestUtils.md5Hex(param);
log.info("签名:{}", sign);
// 签名比对
if (!sign.equals(apiRequest.getSign())) {
throw new ApiException("S50", "签名验证失败");
}
}
//endregion
}
虽然功能是可以实现,但是缺点也很明显,这个类接近300行,包含了众多的业务逻辑,灵活性和扩展性明显是个短板,我们是不是可以借鉴netty优秀的设计思路,采用职责链的设计模式,来实现接口处理器的灵活装配和数据处理呢?
接下来我们将进行这样的改造。