范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

SpringBoot对接外部接口,一步一步性能调实战篇

  需求分析:
  本平台对接某某平台的接口,保证接口的稳定性和安全性 实战:
  首先我们初始化一个Demo,SpringBoot初始化教程略,初始化后的效果如下:
  1.引入依赖
  这里我们使用 commons-httpclient 3       commons-httpclient     commons-httpclient     3.1        com.google.code.gson     gson     2.8.6       org.projectlombok     lombok  2.编写工具类(HttpClientUtils):
  Get请求:
  根据需求,这里我们需要两个参数,一个是token,一个是url参数 + url地址,权限验证采用的是Bearer Tokenpublic static String sendGet(String urlParam, String token) {     // 1.创建httpClient实例对象     HttpClient httpClient = new HttpClient();     // 设置httpClient连接主机服务器超时时间:15000毫秒     httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(15000);     // 2.创建GetMothod实例对象     GetMethod getMethod = new GetMethod(urlParam);     // 3.设置post请求超时时间、请求头     getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 60000);     getMethod.addRequestHeader("Content-Type", "application/json");     if(!StringUtils.isEmpty(token)) {         Header header = new Header("Authorization", "Bearer " + token);         getMethod.addRequestHeader(header);     }     try {         // 4.执行getMethod,调用http接口         httpClient.executeMethod(getMethod);         // 5.读取内容[流的形式读取]         InputStream is = getMethod.getResponseBodyAsStream();         BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));         // 采用线程安全的StringBuffer         StringBuffer res = new StringBuffer();         String str= "";         while((str = br.readLine()) != null){             res.append(str);         }         return res.toString();     } catch (IOException e) {         e.printStackTrace();     } finally {         // 6.释放连接         getMethod.releaseConnection();     }     return null; }
  Post请求:
  根据需求,这里我们需要三个参数,一个是token,一个是url参数 + url地址,还有一个是请求体,权限验证采用的是Bearer Tokenpublic static String sendPost(String urlParam, Map jsonMap, String token) {     // 1.创建httpClient实例对象     HttpClient httpClient = new HttpClient();     // 设置httpClient连接主机服务器超时时间:15000毫秒     httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(15000);     // 2.创建PostMethod实例对象     PostMethod postMethod = new PostMethod(urlParam);     // 设置post请求超时时间、请求头     postMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 60000);     postMethod.addRequestHeader("Content-Type", "application/json;charset=utf-8");     if(!StringUtils.isEmpty(token)) {         Header header = new Header("Authorization", "Bearer " + token);         postMethod.addRequestHeader(header);     }     // 3.设置请求体     Gson gson = new Gson();     String jsonStr = gson.toJson(jsonMap);     postMethod.setRequestBody(jsonStr);     try {         // 4.执行postMethod,调用http接口         httpClient.executeMethod(postMethod);         // 5.读取内容[流的形式读取]         InputStream is = postMethod.getResponseBodyAsStream();         BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));         // 采用线程安全的StringBuffer         StringBuffer res = new StringBuffer();         String str= "";         while((str = br.readLine()) != null){             res.append(str);         }         return res.toString();     } catch (IOException e) {         e.printStackTrace();     } finally {         // 7.释放连接         postMethod.releaseConnection();     }     return null; }
  Main方法测试:// 1.调用获取token接口 String baseUrl = "http://*****/"; String url = baseUrl + "/****/token"; Map jsonMap = new HashMap<>(); jsonMap.put("username", "***"); jsonMap.put("password", "***"); String res = sendPost(url, jsonMap, null); log.info("获得的请求结果:{}", res);
  获得的请求结果:{"message":"success","status":1,"data":{"token":"eyJh****"}}
  我们请求得到JSON字符串后,使用GSON来解析JSON,提取有用的信息,如token// 2.解析JSON,得到token Gson gson = new Gson(); // 克服泛型类型擦除问题 // 具体查阅https://zditect.com/main-advanced/java/gson-json-to-map.html Type mapType = new TypeToken>(){}.getType(); HashMap resMap = gson.fromJson(res, mapType); log.info("请求结果解析:{}", resMap); LinkedTreeMap data = (LinkedTreeMap) resMap.get("data"); String token = (String) data.get("token"); log.info("token为:{}", token);
  获取得到token后我们开始使用,模拟一次Get请求:url = baseUrl + "******"; log.info("获得的请求结果:{}", sendGet(url, token));   获得的请求结果: {"message":"success","status":1,"data":{****}
  完整工具类,方便大家拿来直接使用:
  HttpClientUtils.javapackage com.example.demo;  import com.google.gson.Gson; import com.google.gson.internal.LinkedTreeMap; import com.google.gson.reflect.TypeToken; import lombok.extern.slf4j.Slf4j; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.params.HttpMethodParams; import org.springframework.util.StringUtils;  import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map;  /**  * @author xh  * @Date 2022/9/14  */ @Slf4j public class HttpClientUtils {     public static String sendPost(String urlParam, Map jsonMap, String token) {         // 1.创建httpClient实例对象         HttpClient httpClient = new HttpClient();         // 设置httpClient连接主机服务器超时时间:15000毫秒         httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(15000);         // 2.创建PostMethod实例对象         PostMethod postMethod = new PostMethod(urlParam);         // 设置post请求超时时间、请求头         postMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 60000);         postMethod.addRequestHeader("Content-Type", "application/json;charset=utf-8");         if(!StringUtils.isEmpty(token)) {             Header header = new Header("Authorization", "Bearer " + token);             postMethod.addRequestHeader(header);         }         // 3.设置请求体         Gson gson = new Gson();         String jsonStr = gson.toJson(jsonMap);         postMethod.setRequestBody(jsonStr);         try {             // 4.执行postMethod,调用http接口             httpClient.executeMethod(postMethod);             // 5.读取内容[流的形式读取]             InputStream is = postMethod.getResponseBodyAsStream();             BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));             // 采用线程安全的StringBuffer             StringBuffer res = new StringBuffer();             String str= "";             while((str = br.readLine()) != null){                 res.append(str);             }             return res.toString();         } catch (IOException e) {             e.printStackTrace();         } finally {             // 7.释放连接             postMethod.releaseConnection();         }         return null;     }      public static String sendGet(String urlParam, String token) {         // 1.创建httpClient实例对象         HttpClient httpClient = new HttpClient();         // 设置httpClient连接主机服务器超时时间:15000毫秒         httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(15000);         // 2.创建GetMothod实例对象         GetMethod getMethod = new GetMethod(urlParam);         // 3.设置post请求超时时间、请求头         getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 60000);         getMethod.addRequestHeader("Content-Type", "application/json");         if(!StringUtils.isEmpty(token)) {             Header header = new Header("Authorization", "Bearer " + token);             getMethod.addRequestHeader(header);         }         try {             // 4.执行getMethod,调用http接口             httpClient.executeMethod(getMethod);             // 5.读取内容[流的形式读取]             InputStream is = getMethod.getResponseBodyAsStream();             BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));             // 采用线程安全的StringBuffer             StringBuffer res = new StringBuffer();             String str= "";             while((str = br.readLine()) != null){                 res.append(str);             }             return res.toString();         } catch (IOException e) {             e.printStackTrace();         } finally {             // 6.释放连接             getMethod.releaseConnection();         }         return null;     }      public static void main(String[] args) {         // 1.调用获取token接口         String baseUrl = "http://****";         String url = baseUrl + "/*****";         Map jsonMap = new HashMap<>();         jsonMap.put("username", "****");         jsonMap.put("password", "*****");         String res = sendPost(url, jsonMap, null);         log.info("获得的请求结果:{}", res);         // 2.解析JSON,得到token         Gson gson = new Gson();         // 克服泛型类型擦除问题         // 具体查阅https://zditect.com/main-advanced/java/gson-json-to-map.html         Type mapType = new TypeToken>(){}.getType();         HashMap resMap = gson.fromJson(res, mapType);         log.info("请求结果解析:{}", resMap);         LinkedTreeMap data = (LinkedTreeMap) resMap.get("data");         String token = (String) data.get("token");         log.info("token为:{}", token);         // 3.模拟Get请求         // TODO 需要使用URL编码         url = baseUrl + "****";         log.info("获得的请求结果:{}", sendGet(url, token));     } }
  为前端提供接口并测试:
  首先我们统一返回风格:Result.java package com.example.demo;  import lombok.Data;  import java.io.Serializable;  /**  * @author xh  * @Date 2022/9/14  */ @Data public class Result implements Serializable {     private static final long serialVersionUID = 1L;     /**      * 编码:0表示成功,其他值表示失败      */     private int code = 0;     /**      * 消息内容      */     private String msg = "success";     /**      * 响应数据      */     private T data;      public Result ok(T data) {         this.setData(data);         return this;     }      public Result error(String msg) {         this.code = 500;         this.msg = msg;         return this;     } }
  新建ApiController:
  首先我们将公共变量做一个提取:public static String TOKEN = "";      public static final String BASE_URL = "http://****";      public static final String USERNAME = "****";      public static final String PASSWORD = "****";  // 静态代码块     static {         // 1.调用获取token接口         String url = BASE_URL + "/****";         Map jsonMap = new HashMap<>();         jsonMap.put("username", USERNAME);         jsonMap.put("password", PASSWORD);         String res = sendPost(url, jsonMap, null);         // 2.解析JSON,得到token         Gson gson = new Gson();         Type mapType = new TypeToken>(){}.getType();         HashMap resMap = gson.fromJson(res, mapType);         LinkedTreeMap data = (LinkedTreeMap) resMap.get("data");         TOKEN = (String) data.get("token");         log.info("token获取成功:{}", TOKEN);     }
  模拟Get请求:/**  * Get请求  * 请求地址:http://localhost:8080/identity/getDetail_get?handle=xxx  * @return  */ @GetMapping("/getDetail_get") public Result getDataGet(@RequestParam String handle) {     log.info("开始发起Get请求, token为:{}", TOKEN);     Assert.notNull(handle);     String url = BASE_URL + "/xxx=" + handle;     try {         String res = sendGet(url, TOKEN);         return new Result().ok(res);     } catch (Exception e) {         e.printStackTrace();         return new Result().error("请求失败!");     } }
  模拟Post请求:/**  * 模拟POST请求  * 请求地址:http://localhost:8080/identity/getDetail_post  */ @PostMapping("/getDetail_post") public Result getDataPost(@RequestBody HashMap requestBody) {     String url = BASE_URL + "/****";     try {         String res = sendPost(url, requestBody, TOKEN);;         return new Result().ok(res);     } catch (Exception e) {         e.printStackTrace();         return new Result().error("请求失败!");     } }
  整体代码:package com.example.demo;  import com.google.gson.Gson; import com.google.gson.internal.LinkedTreeMap; import com.google.gson.reflect.TypeToken; import lombok.extern.slf4j.Slf4j; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.*;  import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map;  import static com.example.demo.HttpClientUtils.sendGet; import static com.example.demo.HttpClientUtils.sendPost;  /**  * @author xh  * @Date 2022/9/14  */ @RestController @RequestMapping("/identity/") @Slf4j public class ApiController {     public static String TOKEN = "";      public static final String BASE_URL = "http://*****";      public static final String USERNAME = "****";      public static final String PASSWORD = "****";      // 静态代码块     static {         // 1.调用获取token接口         String url = BASE_URL + "/identity/token";         Map jsonMap = new HashMap<>();         jsonMap.put("username", USERNAME);         jsonMap.put("password", PASSWORD);         String res = sendPost(url, jsonMap, null);         // 2.解析JSON,得到token         Gson gson = new Gson();         Type mapType = new TypeToken>(){}.getType();         HashMap resMap = gson.fromJson(res, mapType);         LinkedTreeMap data = (LinkedTreeMap) resMap.get("data");         TOKEN = (String) data.get("token");         log.info("token获取成功:{}", TOKEN);     }      /**      * Get请求      * 请求地址:http://localhost:8080/identity/getDetail_get?handle=****      * @return      */     @GetMapping("/getDetail_get")     public Result getDataGet(@RequestParam String handle) {         log.info("开始发起Get请求, token为:{}", TOKEN);         Assert.notNull(handle);         String url = BASE_URL + "/****" + handle;         try {             String res = sendGet(url, TOKEN);             return new Result().ok(res);         } catch (Exception e) {             e.printStackTrace();             return new Result().error("请求失败!");         }     }      /**      * 模拟POST请求      * 请求地址:http://localhost:8080/identity/getDetail_post      */     @PostMapping("/getDetail_post")     public Result getDataPost(@RequestBody HashMap requestBody) {         String url = BASE_URL + "/****";         try {             String res = sendPost(url, requestBody, TOKEN);;             return new Result().ok(res);         } catch (Exception e) {             e.printStackTrace();             return new Result().error("请求失败!");         }     } } 优化:
  模拟场景:在尽可能的不破坏源代码的情况下,不喜勿喷优化一:属性通过配置文件读取
  新建application.yml文件api:   baseUrl: http://*****   username: ****   password: ****
  新建配置文件读取类:
  ApiConfig.javapackage com.example.demo;  import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component;   /**  * @author xh  * @Date 2022/9/14  */ @Component @ConfigurationProperties(prefix = "api") @Data public class ApiConfig {      /**      * API地址      */     private String baseUrl;      /**      * 代理用户名      */     private String username;      /**      * 代理密码      */     private String password; }
  ApiController进行微调:@Autowired ApiConfig apiConfig;  public static String TOKEN = "";  public static String BASE_URL = "";  public static String USERNAME = "";  public static String PASSWORD = "";  @PostConstruct private void getBaseInfo() {     BASE_URL = apiConfig.getBaseUrl();     USERNAME = apiConfig.getUsername();     PASSWORD = apiConfig.getPassword(); }  private String getToken() {     if(!StringUtils.isEmpty(TOKEN)) {         return TOKEN;     }     // 1.调用获取token接口     String url = BASE_URL + "/***/token";     Map jsonMap = new HashMap<>();     jsonMap.put("username", USERNAME);     jsonMap.put("password", PASSWORD);     String res = sendPost(url, jsonMap, null);     // 2.解析JSON,得到token     Gson gson = new Gson();     Type mapType = new TypeToken>(){}.getType();     HashMap resMap = gson.fromJson(res, mapType);     LinkedTreeMap data = (LinkedTreeMap) resMap.get("data");     TOKEN = (String) data.get("token");     log.info("token获取成功:{}", TOKEN);     return TOKEN; }
  由于Token会存在过期时间,所以我们这里引用Redis引入依赖:       org.springframework.boot     spring-boot-starter-data-redis  在application.yml添加redis配置: spring:   # redis 配置   redis:     # 地址     host: xxxxx     # 端口,默认为xxx     port: xxxx     # 数据库索引(db0,db1,db2...不同业务可以放在不同数据库中)     database: 0     # 密码     password: "xxxx" 注入RedisTemplate,并优化 @Autowired RedisTemplate redisTemplate;  private String getToken() {     ValueOperations operations = redisTemplate.opsForValue();     // 0.查询Redis     if(!StringUtils.isEmpty(operations.get("token"))) {         return operations.get("token");     }     // 1.调用获取token接口     String url = BASE_URL + "/***/token";     Map jsonMap = new HashMap<>();     jsonMap.put("username", USERNAME);     jsonMap.put("password", PASSWORD);     String res = sendPost(url, jsonMap, null);     // 2.解析JSON,得到token     Gson gson = new Gson();     Type mapType = new TypeToken>(){}.getType();     HashMap resMap = gson.fromJson(res, mapType);     LinkedTreeMap data = (LinkedTreeMap) resMap.get("data");     String token = (String) data.get("token");     // 设置TOKEN 6小时过期     operations.set("token", token, 6, TimeUnit.HOURS);     log.info("token获取成功:{}", token);     return token; } 进一步优化
  场景:如果有大量请求同时访问一个正好过期的缓存数据,可能会出现缓存击穿,所以我们的解决方案是添加分布式锁加入依赖:        org.redisson     redisson     3.11.0        org.springframework.boot     spring-boot-starter-data-redis   创建RedissionConfig.java 配置RedissionClient package com.example.demo;  import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component;  /**  * @author xh  * @Date 2022/9/14  */ @Component public class RedissionConfig {     /**      * 所有对redisson的使用都是通过RedissonClient对象      */     @Bean(destroyMethod="shutdown")     public RedissonClient redisson() {         //创建配置         Config config = new Config();         //可以用"rediss://"来启用SSL连接,useSingleServer表示单例模式         config.useSingleServer().setAddress("redis://xxxx:xxxx").setDatabase(0).setPassword("xxxx");         //根据config创建出RedissonClient实例         return Redisson.create(config);     }  } 注入并编写读锁、写锁: @Autowired RedisTemplate redisTemplate;  @Autowired RedissonClient redisson;  private String getToken() {     // 0.查询Redis     String token = readToken();     if(!StringUtils.isEmpty(token)) {         return token;     }     // 1.调用获取token接口     String url = BASE_URL + "/xxx/token";     Map jsonMap = new HashMap<>();     jsonMap.put("username", USERNAME);     jsonMap.put("password", PASSWORD);     String res = sendPost(url, jsonMap, null);     // 2.解析JSON,得到token     Gson gson = new Gson();     Type mapType = new TypeToken>(){}.getType();     HashMap resMap = gson.fromJson(res, mapType);     LinkedTreeMap data = (LinkedTreeMap) resMap.get("data");     token = (String) data.get("token");     // 设置TOKEN     Assert.isTrue(setToken(token));     log.info("token获取成功:{}", token);     return token; }  private String readToken() {     RReadWriteLock lock = redisson.getReadWriteLock("token-lock");     RLock rLock = lock.readLock();     String token = "";     try {         //加读锁         rLock.lock();         token = redisTemplate.opsForValue().get("token");     } catch (Exception e) {         e.printStackTrace();     } finally {         rLock.unlock();     }     return token; }  private boolean setToken(String token) {     RReadWriteLock lock = redisson.getReadWriteLock("token-lock");     RLock rLock = lock.writeLock();     try {         // 改数据加写锁,读数据加读锁         rLock.lock();         redisTemplate.opsForValue().set("token", token, 6, TimeUnit.HOURS);     } catch (Exception e) {         e.printStackTrace();     } finally {         rLock.unlock();     }     return true; } 再次优化:
  互联网系统经常会遇到高并发大流量的请求,在突发情况下(如秒杀、抢购),瞬间大流量会直接把系统打垮,为了防止出现这种情况最常见的解决方案之一就是限流,当请求达到一定的并发数或速率,就进行等待、排队、降级、拒绝服务等。
  基于Guava工具类【令牌桶算法】,借助自定义注解+AOP实现接口限流
  令牌桶算法的原理也比较简单:系统会维护一个令牌(token)桶,以一个恒定的速度往桶里放入令牌(token),这时如果有请求进来想要被处理,则需要先从桶里获取一个令牌(token),当桶里没有令牌(token)可取时,则该请求将被拒绝服务。令牌桶算法通过控制桶的容量、发放令牌的速率,来达到对请求的限制。单机模式模拟:添加依赖:       com.google.guava     guava     30.1-jre        org.springframework.boot     spring-boot-starter-aop  自定义限流注解:
  Limit.javapackage com.example.demo;  import java.lang.annotation.*; import java.util.concurrent.TimeUnit;  /**  * @author xh  * @Date 2022/9/15  */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Documented public @interface Limit {     /**      * 资源的key,唯一      * 作用:不同的接口,不同的流量控制      */     String key() default "";      /**      * 最多的访问限制次数      */     double permitsPerSecond () ;      /**      * 获取令牌最大等待时间      */     long timeout();      /**      * 获取令牌最大等待时间,单位(例:分钟/秒/毫秒) 默认:毫秒      */     TimeUnit timeunit() default TimeUnit.MILLISECONDS;      /**      * 得不到令牌的提示语      */     String msg() default "系统繁忙,请稍后再试."; } 使用AOP切面拦截限流注解 package com.example.demo;  import com.google.common.collect.Maps; import com.google.common.util.concurrent.RateLimiter; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes;  import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map;  /**  * @author xh  * @Date 2022/9/15  */ @Slf4j @Aspect @Component public class LimitAop {     /**      * 不同的接口,不同的流量控制      * map的key为 Limiter.key      */     private final Map limitMap = Maps.newConcurrentMap();      @Around("@annotation(com.example.demo.Limit)")     public Object around(ProceedingJoinPoint joinPoint) throws Throwable{         MethodSignature signature = (MethodSignature) joinPoint.getSignature();         Method method = signature.getMethod();         //拿limit的注解         Limit limit = method.getAnnotation(Limit.class);         if (limit != null) {             //key作用:不同的接口,不同的流量控制             String key=limit.key();             RateLimiter rateLimiter = null;             //验证缓存是否有命中key             if (!limitMap.containsKey(key)) {                 // 创建令牌桶                 rateLimiter = RateLimiter.create(limit.permitsPerSecond());                 limitMap.put(key, rateLimiter);                 log.info("新建了令牌桶={},容量={}",key,limit.permitsPerSecond());             }             rateLimiter = limitMap.get(key);             // 拿令牌             boolean acquire = rateLimiter.tryAcquire(limit.timeout(), limit.timeunit());             // 拿不到命令,直接返回异常提示             if (!acquire) {                 log.debug("令牌桶={},获取令牌失败",key);                 this.responseFail(limit.msg());                 return null;             }         }         return joinPoint.proceed();     }      /**      * 直接向前端抛出异常      * @param msg 提示信息      */     private void responseFail(String msg)  {         HttpServletResponse resp=((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();         resp.setCharacterEncoding("utf-8");         resp.setContentType("application/json; charset=utf-8");         PrintWriter writer = null;         try {             writer = resp.getWriter();         } catch (IOException e) {             e.printStackTrace();         }         writer.write(new Result().error(msg).toString());     } } 给需要限流的接口加上注解 /**  * Get请求  * @return  */ @GetMapping("/getDetail_get") @Limit(key = "limit1", permitsPerSecond = 1, timeout = 1000, timeunit = TimeUnit.MILLISECONDS, msg = "当前排队人数较多,请稍后再试!") public Result getDataGet(@RequestParam String handle) {     log.info("开始发起Get请求, token为:{}", getToken());     Assert.notNull(handle);     String url = BASE_URL + "/****" + handle;     try {         String res = sendGet(url, getToken());         return new Result().ok(res);     } catch (Exception e) {         e.printStackTrace();         return new Result().error("请求失败!");     } }
  多次请求时:
  来源:blog.csdn.net/m0_51517236/article/details/126855104

日本CPB洗面奶,我已经空瓶了7瓶的洗面奶推荐给你!谁不想要一张干净白嫩的脸呢?脸上干干净净的,化妆的步骤都少了很多,出门也更加有自信了!但问题是我们的皮肤无时无刻在受到攻击,细胞受损,脸上松弛发黄长细纹。这一切的关键都基于基础清洁PRADAMOON手袋2023春夏系列全新PradaMoon手袋透过现代视角,概念化重塑千禧年代标志风格,自品牌典藏档案中凝炼独有特质,见证Prada过往当下与未来。Moon手袋从多元化世界汲取灵感,集品牌鲜明形象数十西热下血本,请同曦全队吃烤全羊,林葳口水直流,邬挺嘉跑步赶来昨天是CBA南京同曦兔年开年回来训练的第一天,新年新气象,作为球队主教练的西热力江为了更好的激励球队的士气,下血本请全队吃地道的新疆烤全羊,从晒出的视频可以看到,烤好的整只全羊被铺NBA本赛季哨子榜单,前十几乎全是新生代!为了让新生代接班,NBA已经无所不用其极,近期一家美国媒体公布了一项数据,他们统计了本赛季突破得到哨子的概率排名,其中詹姆斯库里等老牌球星几乎都没有上榜。第一位字母哥,场均14。4特林布尔,生日快乐!今天是梅洛特林布尔28岁生日,让我们一起祝福这位控球后卫生日快乐!后场指挥官进攻多面手本赛季加盟大鲨鱼后,特林布尔表现出色。CBA常规赛至今,他场均拿下19。5分,排名全队第二5。汤杰22分5板7助,德勒黑1913,安徽文一男篮3连胜锁定常规赛第二NBL常规赛最后一轮比赛,这也是常规赛倒数第二个比赛日,安徽文一男篮11477大胜福建风之子,安徽文一队锁定常规赛第二,陕西信达常规赛冠军,广西威壮常规赛第三名。安徽文一男篮更换主怎么消耗拆家娃的精力?福州这几个地方可以带娃出游,免费!不知不觉春天的脚步就慢慢的到来了,大家肯定也即将开始苦恼周末应该如何让娃消耗他们比肩永动机一般的精力。本文就给大家推荐几个我自己平时周末带娃游玩的去处,在遛娃的同时也可以放松自己的萌娃多次想跳下台阶被薅回,网友孩子的快乐是跳下去,舅舅的是拉起来近日,常德的朱女士分享了一则视频,视频中萌娃多次想跳下台阶,都被舅舅一把薅回来。朱女士介绍,当天上午,2岁半的小外甥多米想去田里玩,出于安全,家人不想让他下到田里,我是小孩的小姨,发电量提高18倍!麻省理工学院开发超薄轻质光伏电池近日,麻省理工学院(MIT)的研究人员开发了一种超薄轻质光伏电池,这种光伏电池每千克的发电量是传统光伏电池板的18倍,并且可以很容易地部署到各种物体的表面。据报道,这种柔韧且耐用的12月哪吒V销量断崖式下跌,销量造假实锤!据相关媒体报道,各大车企厂商陆续公布了2022年12月及全年销量成绩。在这场收官之战中,身属新能源第一梯队蔚小理表现优异,12月销量均破万,成为新势力销量排行榜前三甲。而第二梯队新战局一片混乱三款纯电中大型SUV车型推荐随着新能源车型逐渐登上历史舞台,无论是主动选择还是被动接受,如今的纯电动车确实已经占据了相当一部分市场份额。除了在最为走量的中低端市场中大杀四方,电动车的触手也非常热衷于伸向利润更
情人之间,有这三种感觉,才是真爱情人之间,有这三种感觉,才是真爱红尘滚滚,渺渺人生,在我们的生命中,谁都想拥有一个真心爱我们的人和我们一起在漫长的人生里,慢慢度完一生。那份爱,不需轰轰烈烈,也不用刻意讨好,只需你郝劭文一家三口同框,妻子穿情侣装扮嫩,他当合格继父与女儿腻歪有了身份的变化,对于服装的风格和需求也产生了相应的变化。郝邵文和妻子的从求婚到结婚一直都蛮高调的,两个人在结婚之后依然非常的恩爱,一家三口同框时,二婚妻子林宁瑞两人身着情侣装扮嫩,换个口味吃主食糙米所含的B族维生素,比白米多四五倍,白米中的维生素B1煮熟后全部消失,糙米则能保留80,且脂肪也是白米的四倍(不用担心,这是有益的脂肪)。维生素B1参与糖代谢,帮助人体对碳水化合原来前辈高手们是这样判断松的感觉的他姓王,是孙式武学第四代传承人,我在孙氏太极拳一书中拜读过他论述形意拳八卦掌和太极推手的三篇文章,论孙式太极推手他的水平在我们县应该是一流的,有人说他最厉害,也有人会推举他人。那天世界杯仅剩2个月,葡萄牙队做出争议决定!C罗遭打击,梅西偷着乐在世界杯仅剩2个月的情况下,葡萄牙队为了备战世界杯前的最后一个国际比赛日,已经公布了球队的比赛大名单。可以确定的是,这一阵容基本上就是葡萄牙队征战世界杯时的阵容了。对此,外界一致认iPhone14系列内置壁纸分享安卓也适用,换完感觉像买了新手机文凯尔资讯iPhone14系列终于开卖,16号开始首批用户也将陆陆续续收到新机了。今天给大家分享一组iPhone14系列内置原生壁纸,如果你是一位安卓用户,或者是iPhone老用户综合评论新款西铁城系列8870负担得起的替代品我们之前已经在这些页面中详细介绍了新的机械公民。这些手表将于2021年9月发布,随着发布的临近,我们推出了第二款国际型号8系870型。今年对西铁城来说是有趣的一年。他们已经加强了他哪家电视盒子更强,华为中兴天猫小米四款盒子拆解对比本内容来源于什么值得买APP,观点仅代表作者本人作者科技陈这几天清理杂物房又清理出几个以前购买的几款主流电视盒子,所以心血来潮想拆解对比一下哪款盒子性能更强,本次对比的四款电视价格话说你妈做的饭,到底好吃不好吃?妈妈的厨艺,重新定义了温暖这个词。它像是一只手,把你一直往前奔的那个身体拖住了,拽一拽,拽回来,然后说孩子再忙,也要好好吃饭。爱你的人,总想把最好的味道留给你。谢谢妈妈,这么多年爱选择零食有讲究,别老吃薯片甜品,5种零食记得囤,好吃又营养美食人人皆爱,然而一样菜品是否美味,每个人都有不一样的评判标准,零食的选择亦是如此。随着生活水平的提升,人们时常会在家里囤上一些自己爱吃的薯片甜品等零食,既是物质支撑也是精神慰藉。春吃芽夏吃瓜秋吃果冬吃根秋吃这三果,好吃润燥不上火养生乃长寿之伴侣,健康是长寿的朋友,民间有句谚语,春吃芽夏吃瓜秋吃果冬吃根现在也已经进入了秋天,正是丰收的季节,在这个季节我要多注意养生,可以吃一些应季的水果蔬菜。大家都知道秋天的