测试接口1、失败后重试0、

发布时间:2025-06-24 19:46:51  作者:北方职教升学中心  阅读量:262


Hutool的HttpUtil

  • 6、Apache的HttpClient

    后续这些方式,本质上就是对java.net包的一个封装了。SpringBoot的RestTemplate

    使用SpringBoot封装的RestTemplate,依赖写web的:

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>

    把RestTemplate的Bean放到IoC容器中:

    @ConfigurationpublicclassRestTemplateConfig{@BeanpublicRestTemplaterestTemplate(){returnnewRestTemplate();}}

    3.1 GET

    发送Get请求,常用方法:

    • getForObject
    • getForEntity
    /*** url为请求的地址* responseType为请求响应body的类型* urlVariables为url中的参数绑定* */getForEntity(Stringurl,ClassresponseType,Object…urlVariables)/*** URI对象来替代之前getForEntity的url和urlVariables参数来指定访问地址和参数绑定* URI是JDK java.net包下的一个类* */getForEntity(URIurl,ClassresponseType)

    示例:

    @SpringBootTestclassLearningApplicationTests{@ResourceprivateRestTemplaterestTemplate;@TestvoidcontextLoads(){Stringurl ="http://localhost:8080/svc1/t1?name={name}";// 参数Map<String,String>paramMap =newHashMap<>();paramMap.put("name","Tom");ResponseEntity<String>responseEntity =restTemplate.getForEntity(url,String.class,paramMap);// 状态码HttpStatusstatusCode =responseEntity.getStatusCode();// 响应Stringbody =responseEntity.getBody();System.out.println(statusCode +body);}}

    接口路径不用字符串,改为URI对象:

    @TestvoidtestTemplate(){Stringurl ="http://localhost:8080/svc1/t1";Stringname ="Tom";// 使用 UriComponentsBuilder 构建 URLURIuri =UriComponentsBuilder.fromHttpUrl(url).queryParam("name",name).build().toUri();ResponseEntity<String>responseEntity =restTemplate.getForEntity(uri,String.class);// 状态码HttpStatusstatusCode =responseEntity.getStatusCode();// 响应Stringbody =responseEntity.getBody();System.out.println(statusCode +body);}

    最后,getForObject:

    getForObject(Stringurl,ClassresponseType,Object...urlVariables)getForObject(Stringurl,ClassresponseType,MapurlVariables)getForObject(URIurl,ClassresponseType)

    和getForEntity的区别是,getForObject只有一个响应的内容,响应码、JDK的HttpURLConnection

  • 2、测试接口

    写两个测试接口,一个GET,一个POST

    @RestController@RequestMapping("/svc1")publicclassController{@GetMapping("/t1")publicStringdoGet(@RequestParam(required =false)Stringname){return"test"+name;}@PostMapping("/t2")publicResultVodoPost(@RequestBodyRequestBodyDtodto,@RequestParamStringkey){returnnewResultVo(200,"操作成功",dto.getName()+dto.getChoose()+key);}}

    1、先引入Apache做http请求的依赖坐标:

    <dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.13</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpcore</artifactId><version>4.4.16</version></dependency>
    publicclassTestDemo{publicstaticvoidmain(String[]args){try(CloseableHttpClienthttpClient =HttpClients.createDefault()){// 创建POST请求对象HttpPosthttpPost =newHttpPost("http://localhost:8080/svc1/t2?key=abc");// 设置请求头httpPost.setHeader("Content-Type","application/json; utf-8");httpPost.setHeader("Accept","application/json");// 设置请求体RequestBodyDtodto =newRequestBodyDto("Tom","A");Stringjson =JSON.toJSONString(dto);StringEntityentity =newStringEntity(json);httpPost.setEntity(entity);// 执行请求并获取响应CloseableHttpResponseresponse =httpClient.execute(httpPost);HttpEntityresponseEntity =response.getEntity();// 处理响应if(null!=responseEntity){StringresponseStr =EntityUtils.toString(responseEntity);System.out.println(responseStr);// 也可按需把json串反序列化成Java对象,略}}catch(IOExceptione){e.printStackTrace();}}}

    3、Apache的HttpClient

  • 3、响应头等没有

    在这里插入图片描述

    3.2 POST

    常用方法:

    • postForEntity
    • postForObject
    • postForLocation

    以postForEntity为例,其参数可选:(重载)

    postForEntity(Stringurl,Objectrequest,ClassresponseType,Object...uriVariables)postForEntity(Stringurl,Objectrequest,ClassresponseType,MapuriVariables)postForEntity(URIurl,Objectrequest,ClassresponseType)

    示例:

    @TestvoidtestTemplate2(){Stringurl ="http://localhost:8080/svc1/t2?key=Tom";RestTemplaterestTemplate =newRestTemplate();// 请求头HttpHeadersheaders =newHttpHeaders();headers.add(HttpHeaders.AUTHORIZATION,"Bear xx");// headers.set("Content-Type", "application/x-www-form-urlencoded");headers.add(HttpHeaders.CONTENT_TYPE,"application/json");// 创建请求体对象并放入数据HttpEntity<RequestBodyDto>requestData =newHttpEntity<>(newRequestBodyDto("Tom","A"),headers);// 和postForEntity一个意思ResponseEntity<String>responseEntity =restTemplate.exchange(url,HttpMethod.POST,requestData,String.class);// 获取响应状态码和响应体HttpStatusstatusCode =responseEntity.getStatusCode();StringresponseBody =responseEntity.getBody();System.out.println(statusCode +" "+responseBody);}

    4、SpringBoot的RestTemplate

    • 3.1 GET
    • 3.2 POST
  • 4、Hutool的HttpUtil

    还是对 java.net的封装,引入依赖:

    <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.16</version><!-- 请检查最新版本 --></dependency>

    处理GET和POST:

    /** * @param url           baseUrl * @param requestMethod 请求方式 * @param headerMap     请求头参数key-value * @param paramMap      路径参数key-value,形如?name=Tom&country=Chain * @param bodyJsonStr   post的body传参,json字符串 * @return 响应体 */publicstaticStringsendRequest(Stringurl,MethodrequestMethod,Map<String,String>headerMap,Map<String,Object>paramMap,StringbodyJsonStr){// 路径参数不为空时,拼接URLif(paramMap !=null){UrlBuilderurlBuilder =UrlBuilder.of(url);paramMap.forEach((k,v)->urlBuilder.addQuery(k,v));url =urlBuilder.toString();}//发送请求HttpResponsehttpResponse =HttpUtil.createRequest(requestMethod,url).addHeaders(headerMap).body(bodyJsonStr).execute();returnhttpResponse.body();}

    测试下:

    @TestvoidtestHuTool(){Stringurl ="http://localhost:8080/svc1/t1";Map<String,Object>paramMap =newHashMap<>();paramMap.put("name","Tom");Map<String,String>headerMap =newHashMap<>();headerMap.put("Authorization","Bear xx");Stringresponse =sendRequest(url,Method.GET,headerMap,paramMap,null);System.out.println(response);}@TestvoidtestHuTool2(){Stringurl ="http://localhost:8080/svc1/t2";Map<String,Object>paramMap =newHashMap<>();paramMap.put("key","Tom");Map<String,String>headerMap =newHashMap<>();headerMap.put("Authorization","Bear xx");RequestBodyDtodto =newRequestBodyDto("Tom","A");StringbodyJsonStr =JSON.toJSONString(dto);Stringresponse =sendRequest(url,Method.POST,headerMap,paramMap,bodyJsonStr);System.out.println(response);}

    6、

    文章目录

    • 0、JDK的HttpURLConnection

    原生版,主要依靠JDK的 java.net包,GET请求:

    importjava.net.HttpURLConnection;importjava.io.BufferedReader;importjava.io.IOException;importjava.io.InputStreamReader;importjava.net.URL;@Slf4jpublicclassTestDemo{publicstaticvoidmain(String[]args){BufferedReaderreader =null;try{// 创建URL对象URLurl =newURL("http://localhost:8080/svc1/t1");// 打开连接HttpURLConnectionconnection =(HttpURLConnection)url.openConnection();connection.setRequestMethod("GET");// 读取响应reader =newBufferedReader(newInputStreamReader(connection.getInputStream()));// 处理响应StringinputLine;StringBuilderresponse =newStringBuilder();while((inputLine =reader.readLine())!=null){response.append(inputLine);}System.out.println(response);}catch(Exceptione){log.error("调用失败");e.printStackTrace();}finally{if(reader !=null){try{reader.close();}catch(IOExceptione){e.printStackTrace();}}}}}

    URL类是JDK java.net包下的一个类,表示一个统一资源标识符(Uniform Resource Identifier)引用

    POST请求:

    importjava.io.*;importjava.net.HttpURLConnection;importjava.net.URL;importjava.nio.charset.StandardCharsets;importcom.alibaba.fastjson.JSON;@Slf4jpublicclassTestDemo{publicstaticvoidmain(String[]args){try{// 创建URL对象URLurl =newURL("http://localhost:8080/svc1/t2?key=abc");// 打开连接HttpURLConnectionconnection =(HttpURLConnection)url.openConnection();connection.setRequestMethod("POST");// 设置请求头与数据格式connection.setRequestProperty("Content-Type","application/json; utf-8");connection.setRequestProperty("Accept","application/json");// 允许向服务器写入数据connection.setDoOutput(true);RequestBodyDtodto =newRequestBodyDto("Tom","A");Stringjson =JSON.toJSONString(dto);// 写入JSON到请求体try(OutputStreamos =connection.getOutputStream()){BufferedOutputStreambos =newBufferedOutputStream(os);bos.write(json.getBytes(StandardCharsets.UTF_8));bos.flush();}// 读取响应try(BufferedReaderbr =newBufferedReader(newInputStreamReader(connection.getInputStream(),"UTF-8"))){StringBuilderresponse =newStringBuilder();StringresponseLine;while((responseLine =br.readLine())!=null){response.append(responseLine.trim());}System.out.println("Response: "+response.toString());}}catch(Exceptione){e.printStackTrace();}}}

    在这里插入图片描述

    2、返回类型声明正确就行
    主要是基于SpringMVC的注解来声明远程调用的信息,比如:➢服务名称:userservice➢请求方式:GET➢请求路径:/user/{id}➢请求参数:Longid➢返回值类型:User
    • 注入上面定义的FeignClient类,也就是UserClient,直接调用声明的那个方法
    @AutowiredprivateUserClientuserClient;publicOrderqueryOrderById(LongorderId){//查询订单Orderorder =orderMapper.findById(orderId);//利用feign发起http请求,查用户Useruser =userClient.findById(order.getUserId());//封装,对象的某个属性也是个对象,即引用类型order.setUser(user);returnorder;}
    • 被调用方有多个实例时,负载均衡也不用考虑,Feign用了Ribbon做负载均衡
    • 关于Feign请求头的添加,可重写RequestInterceptor的apply方法:
    @ConfigurationpublicclassFeignConfigimplementsRequestInterceptor{@Overridepublicvoidapply(RequestTemplaterequestTemplate){//添加tokenrequestTemplate.header("token","eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJ4ZGFwYXBwaWQiOiIzNDgxMjU4ODk2OTI2OTY1NzYiLCJleHAiOjE2NjEyMjY5MDgsImlhdCI6MTY2MTIxOTcwOCwieGRhcHRlbmFudGlkIjoiMzAwOTgxNjA1MTE0MDUyNjA5IiwieGRhcHVzZXJpZCI6IjEwMDM0NzY2MzU4MzM1OTc5NTIwMCJ9.fZAO4kJSv2rSH0RBiL1zghdko8Npmu_9ufo6Wex_TI2q9gsiLp7XaW7U9Cu7uewEOaX4DTdpbFmMPvLUtcj_sQ");}}
    • 要做降级逻辑的话:如下,调用消息中心服务
    // @FeignClient的fallbackFactory指定下降级逻辑的类@Component@FeignClient(contextId ="remoteMessageService",value =ServiceNameConstants.MESSAGE_SERVICE,fallbackFactory =RemoteMessageFallbackFactory.class)publicinterfaceRemoteMessageService{/**     * 发送定时消息任务:每分钟扫描发送消息     *     * @return 结果     */@GetMapping("/inner/message/sendTimingMessage")publicR<Void>sendTimingMessage();/**     * 发送系统消息     *     * @return 结果     */@PostMapping("/inner/message/sendSystemMessage")publicR<Void>sendSystemMessage(@RequestBodyMessageSendSystemDtomessageSendSystemDto);}
    // 降级逻辑@ComponentpublicclassRemoteMessageFallbackFactoryimplementsFallbackFactory<RemoteMessageService>{privatestaticfinalLoggerlog =LoggerFactory.getLogger(RemoteMessageFallbackFactory.class);@OverridepublicRemoteMessageServicecreate(Throwablethrowable){throwable.printStackTrace();log.error("消息服务调用失败:{}",throwable.getMessage());returnnewRemoteMessageService(){@OverridepublicR<Void>sendTimingMessage(){returnR.fail("调用发送定时消息接口失败:"+throwable.getMessage());}@OverridepublicR<Void>sendSystemMessage(MessageSendSystemDtomessageSendSystemDto){returnR.fail("调用发送消息接口失败:"+throwable.getMessage());}};}}

    5、SpringCloud的Feign

    上面的RestTemplate,在调三方接口时挺好用的,但微服务架构下,各个微服务之间调用时,url就不好写,由此,用Feign:一个声明式的http客户端

    核心思路是声明出:

    • 你调谁
    • 用什么方式
    • 请求参数是啥
    • 返回类型是啥

    引入依赖:

    <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>

    启动类上加上@EnableFeignClients

    //在order的启动类中开启Feign@EnableFeignClients@MapperScan("com.llg.order.mapper")@SpringBootApplicationpublicclassOrderApplication{publicstaticvoidmain(String[]args){SpringApplication.run(OrderApplication.class,args);}}
    • 以order服务调用user服务为例,编写调用方:
    // 远程调用userservice服务@FeignClient("userservice")publicinterfaceUserClient{@GetMapping("/user/{id}")UserfindById(@PathVariable("id")Longid);// 后续接口自行添加}!!findById这个方法名随便起!!调用的接口路径、请求参数、调用的服务名、

    /**  * @param url           baseUrl  * @param requestMethod 请求方式  * @param headerMap     请求头参数key-value  * @param paramMap      路径参数key-value,形如?name=Tom&country=Chain  * @param bodyJsonStr   post的body传参,json字符串  * @return 响应体  */publicstaticStringsendRequest(Stringurl,MethodrequestMethod,Map<String,String>headerMap,Map<String,Object>paramMap,StringbodyJsonStr){// 是否成功标记位booleansuccessFlag =false;// 重试次数累计intretryCount =1;HttpResponsehttpResponse =null;while(!successFlag &&retryCount <=3){try{// 路径参数不为空时,拼接URLif(paramMap !=null){UrlBuilderurlBuilder =UrlBuilder.of(url);paramMap.forEach((k,v)->urlBuilder.addQuery(k,v));url =urlBuilder.toString();}// 发送请求httpResponse =HttpUtil.createRequest(requestMethod,url).addHeaders(headerMap).body(bodyJsonStr).execute();if(httpResponse.getStatus()!=200){retryCount++;}else{successFlag =true;}}catch(Exceptione){e.printStackTrace();retryCount++;}}returnhttpResponse ==null?null:httpResponse.body();}
  • SpringCloud的Feign
  • 5、失败后重试

    考虑到远程调用可能失败,失败后重试三次,以上面的hutool为例来实现,其余的都一样,主要还是一个是否成功标记位 + 一个计数,successFlag不用voilate,并发安全也不用考虑,线程内部调用的,用到的数存栈里了都。测试接口

  • 1、失败后重试