求助!!!兄弟们,这玩意搞了我一天了!!!

API接口项目拓展:

我在客户端写了一个根据接口名调用相对应的接口的测试方法(动态调用接口),如果去调用之前跟着鱼皮做的getNameByJson方法,就可以通过gateway调用到interface中的方法,但是如果去调用自己些的方法,就会无法从gateway中路由到interface中。

我在自定义的接口中加断点,直接到不了这个断点,但是在鱼皮的那个方法中加断点就可以

相关方法:

测试方法

java
复制代码
/** * 测试接口 * * @param interfaceInfoTextRequest * @param request * @return */ // 控制器方法 @PostMapping("/TextInterface2") @Operation(summary = "测试接口2", description = "根据id获取对应的接口") public BaseResponse<Object> TextInterfaceInfo( @Parameter(description = "根据id请求") @RequestBody InterfaceInfoTextRequest interfaceInfoTextRequest, @Parameter(hidden = true) HttpServletRequest request) { // 1、 判断参数是否存在 if (interfaceInfoTextRequest == null || interfaceInfoTextRequest.getId() <= 0) { throw new BusinessException(ErrorCode.PARAMS_ERROR); } // 2、接收请求中的参数 Long id = interfaceInfoTextRequest.getId(); String userRequestParams = interfaceInfoTextRequest.getUserRequestParams(); // 打印用户填写的请求参数 log.info("用户填写的请求参数为:{}", userRequestParams); // 3、判断接口是否存在 InterfaceInfo oldInterfaceInfo = interfaceInfoService.getById(id); if (oldInterfaceInfo == null) { throw new BusinessException(ErrorCode.NOT_FOUND_ERROR); } // 4、检查接口是否为下线状态 if (oldInterfaceInfo.getStatus() == InterfaceInfoStatusEnum.OFFLINE.getValue()) { throw new BusinessException(ErrorCode.PARAMS_ERROR, "接口已关闭"); } // 5、获取当前登录用户 User loginUser = userService.getLoginUser(request); // 6、获取当前用户的AK、SK String accessKey = loginUser.getAccessKey(); String secretKey = loginUser.getSecretKey(); ApiClient textApiClient = new ApiClient(accessKey, secretKey); try { // 7、创建一个JSON解析对象,解析用户传递过来的参数 Gson gson = new Gson(); // 获取接口的方法名和参数类型 String methodName = oldInterfaceInfo.getName(); Class<?> parameterType = getParameterType(methodName); // 需要实现此方法以获取参数类型 // 动态解析用户传递的参数 Object requestBody = gson.fromJson(userRequestParams, parameterType); log.info("解析后的请求参数: {}", requestBody); // 8、构建 RequestParams 对象 String url = oldInterfaceInfo.getUrl(); log.info("用户访问的接口地址为:{}", url); RequestParams<?> requestParams = new RequestParams<>(url, requestBody); // 调用 invokeMethod 方法 ApiResponse<?> jsonResponse = textApiClient.invokeMethod(methodName, requestParams); log.info("API Response: {}", jsonResponse); // 9、返回响应 return ResultUtils.success(jsonResponse); } catch (JsonParseException e) { throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数解析失败"); } catch (NoSuchMethodException e) { throw new BusinessException(ErrorCode.PARAMS_ERROR, "方法不存在"); } catch (Exception e) { throw new RuntimeException(e); } } // 辅助方法:根据方法名获取参数类型 private Class<?> getParameterType(String methodName) throws NoSuchMethodException { // 这里需要根据实际的 API 定义来实现,假设有一个映射表 Map<String, Class<?>> methodParamTypes = new HashMap<>(); methodParamTypes.put("getNameByGet", String.class); methodParamTypes.put("getNameByPost", String.class); methodParamTypes.put("getNameByJson", com.qingmeng.apiclientsdk.entry.User.class); methodParamTypes.put("getDayHistory", MonthDay.class); Class<?> paramType = methodParamTypes.get(methodName); if (paramType == null) { throw new NoSuchMethodException("未知的方法名:" + methodName); } return paramType; }

客户端

java
复制代码
/** * 根据 methodName 调用相应的方法并返回 ApiResponse * * @param methodName 方法名 * @param params 请求参数 * @return ApiResponse 结果 */ public ApiResponse<?> invokeMethod(String methodName, RequestParams<?> params) { try { // 获取方法对象及其参数类型 Method method = getDeclaredMethodByNameAndParams(methodName, params.getBody()); Object result = method.invoke(this, params.getBody()); return ApiResponse.success(result); } catch (Exception e) { return ApiResponse.failure("Error invoking method: " + methodName + ". Error: " + e.getMessage()); } } private Method getDeclaredMethodByNameAndParams(String methodName, Object body) throws NoSuchMethodException { Class<?>[] parameterTypes = new Class<?>[]{body.getClass()}; return this.getClass().getDeclaredMethod(methodName, parameterTypes); } public String getNameByJson(User user){ String json = JSONUtil.toJsonStr(user); HttpResponse httpResponse = null; try { httpResponse = HttpRequest.post(GATEWAY_HOST + "/name1/json") .addHeaders(getHeadersMap(json)) .body(json) .execute(); } catch (Exception e) { throw new RuntimeException("接口调用异常" + e); } if (!httpResponse.isOk()){ throw new RuntimeException("请求失败"); } return httpResponse.body(); } /** * 历史上的今天 * @param monthDay * @return */ public String getDayHistory(MonthDay monthDay){ String json = JSONUtil.toJsonStr(monthDay); Map<String, Object> param = new HashMap<String, Object>(); param.put("month", monthDay.getMonth()); param.put("day", monthDay.getDay()); HttpResponse httpResponse = null; try { httpResponse = HttpRequest.post(GATEWAY_HOST + "/day/history") .addHeaders(getHeadersMap(json)) .form(param) .execute(); } catch (Exception e) { throw new RuntimeException("接口调用异常" + e); } if (!httpResponse.isOk()){ throw new RuntimeException("请求失败"); } return httpResponse.body(); }

网关

java
复制代码
/** * 全局过滤器 * @param exchange * @param chain * @return */ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 1、接收用户的API请求 // 2、记录用户请求日志 ServerHttpRequest request = exchange.getRequest(); log.info("请求唯一标识:" + request.getId()); RequestPath path = request.getPath(); HttpMethod method = request.getMethod(); log.info("请求路径:" + path); log.info("请求方法:" + method); log.info("请求参数:" + request.getQueryParams().toSingleValueMap()); //请求来源地址 InetSocketAddress localAddress = request.getLocalAddress(); log.info("请求来源地址:" + localAddress); ServerHttpResponse response = exchange.getResponse(); // 黑白名单(一般使用白名单,即只允许白名单之内的用户访问) if (!IP_WHITE_LIST.contains(localAddress.getHostName())) { handNoAuth(response); } // 3、判断用户权限(用户鉴权) HttpHeaders headers = request.getHeaders(); String accessKey = headers.getFirst("accessKey"); String body = headers.getFirst("body"); String nonce = headers.getFirst("nonce"); String timestamp = headers.getFirst("timestamp"); String sign = headers.getFirst("sign"); //2、从数据库中查询accessKey,并判断是否有效 User invokerUser = null; try { invokerUser = remoteService.getInvokerUser(accessKey); }catch (Exception e){ return handNoAuth(response); } if (invokerUser == null){ return handNoAuth(response); } //3、校验一下随机数 todo 随机数可以存储在redis中或者用hashmap存储,这里也应该查询而不是比较是否大于10000 if (Long.parseLong(nonce) > 10000) { return handNoAuth(response); } //4、校验时间戳与当前时间的差距,todo 可以使用常量来保存时间 Long currentTIme = System.currentTimeMillis() / 1000; final Long FIVE_MINUTES = 60 * 5L; if ((currentTIme - Long.parseLong(timestamp)) > FIVE_MINUTES) { return handNoAuth(response); } //5、校验签名是否正确 // 这里的secretKey 上面根据ak查询到用户的所有信息 String secretKey = invokerUser.getSecretKey(); String serverSign = SignUtils.getSign(body, secretKey); if (!sign.equals(serverSign)) { return handNoAuth(response); } // 4、判断接口是否存在 //从数据库中查询模拟接口是否存在,以及请求方法是否匹配(还可以校验请求参数) InterfaceInfo interfaceInfo = null; try { interfaceInfo = remoteService.isInvokeMock(path.value(), method.name()); }catch (Exception e){ return handNoAuth(response); } if (interfaceInfo == null){ return handNoAuth(response); } // 5、 判断用户是否还有调用次数 try { boolean b = remoteService.invokeHaveNum(invokerUser.getId(), interfaceInfo.getId()); }catch (Exception e){ log.error("网关捕获异常:调用次数不足"); return handNoHaveNum(response); } // 6、请求转发,调用模拟接口,并返回结果 try { Mono<Void> filter = chain.filter(exchange); }catch (Exception e){ return handError(response); } return handleResponse(exchange, chain, invokerUser.getId(), interfaceInfo.getId()); } /** * 处理响应 * * @param exchange * @param chain * @return */ public Mono<Void> handleResponse(ServerWebExchange exchange, GatewayFilterChain chain,Long userId,Long interfaceInfoId) { try { // 获取原始的响应对象 ServerHttpResponse originalResponse = exchange.getResponse(); // 获取数据缓冲工厂 DataBufferFactory bufferFactory = originalResponse.bufferFactory(); // 获取响应的状态码 HttpStatus statusCode = originalResponse.getStatusCode(); // 判断状态码是否为200 OK(按道理来说,现在没有调用,是拿不到响应码的,对这个保持怀疑 沉思.jpg) if(statusCode == HttpStatus.OK) { // 创建一个装饰后的响应对象(开始穿装备,增强能力) ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) { // 重写writeWith方法,用于处理响应体的数据 // 这段方法就是只要当我们的模拟接口调用完成之后,等它返回结果, // 就会调用writeWith方法,我们就能根据响应结果做一些自己的处理 @Override public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { log.info("body instanceof Flux: {}", (body instanceof Flux)); // 判断响应体是否是Flux类型 if (body instanceof Flux) { Flux<? extends DataBuffer> fluxBody = Flux.from(body); // 返回一个处理后的响应体 // (这里就理解为它在拼接字符串,它把缓冲区的数据取出来,一点一点拼接好) return super.writeWith(fluxBody.map(dataBuffer -> { //8、调用成功,用户接口调用次数+1 invokeCount remoteService.invokeCountNum(userId,interfaceInfoId); // 读取响应体的内容并转换为字节数组 byte[] content = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(content); DataBufferUtils.release(dataBuffer);//释放掉内存 // 构建日志 StringBuilder sb2 = new StringBuilder(200); List<Object> rspArgs = new ArrayList<>(); rspArgs.add(originalResponse.getStatusCode()); //rspArgs.add(requestUrl); String data = new String(content, StandardCharsets.UTF_8);//data sb2.append(data);// 将处理后的内容重新包装成DataBuffer并返回 //打印响应日志 log.info("响应结果:" + data); //设置新的响应码为服务器异常 log.info("响应码: {}, 响应体: {}", statusCode, data); return bufferFactory.wrap(content); })); } else { log.error("响应code异常:", getStatusCode()); } return super.writeWith(body); } }; // 对于200 OK的请求,将装饰后的响应对象传递给下一个过滤器链,并继续处理(设置repsonse对象为装饰过的) return chain.filter(exchange.mutate().response(decoratedResponse).build()); } // 对于非200 OK的请求,直接返回,进行降级处理 //9、todo 调用失败,返回一个规范 的错误码 handError(exchange.getResponse()); return chain.filter(exchange); }catch (Exception e){ // 处理异常情况,记录错误日志 log.error("网关处理响应异常" + e); return chain.filter(exchange); } }

接口

这里用的okhttp,因为刚开始怀疑是hutool请求的问题,结果发现还是不行

Java
复制代码
@RestController @RequestMapping("/day") public abstract class DayController { @PostMapping("/history") public String getDayHistory(@RequestBody MonthDay monthDay) { String month = monthDay.getMonth(); String day = monthDay.getDay(); OkHttpClient client = new OkHttpClient().newBuilder() .build(); Request request = new Request.Builder() .url("https://hot.cigh.cn/calendar/date?month=" + month + "&day=" + day) .get() // 使用.get()方法更简洁 .build(); try (Response response = client.newCall(request).execute()) { if (response.isSuccessful()) { return response.body().string(); } else { throw new RuntimeException("Request failed with code: " + response.code()); } } catch (IOException e) { throw new RuntimeException(e); } } }
0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
下载 APP