轻量级HTTP客户端框架—Forest

简介

Forest 是一个开源的 Java HTTP 客户端框架,它能够将 HTTP 的所有请求信息(包括 URL、Header 以及 Body 等信息)绑定到自定义的 Interface 方法上,能够通过调用本地接口方法的方式发送 HTTP 请求。使用 Forest 就像使用类似 Dubbo 那样的 RPC 框架一样,只需要定义接口,调用接口即可,不必关心具体发送 HTTP 请求的细节。

协议

使用HTTP协议,不需要注册中心

Forest工作原理

Forest会将定义好的接口通过动态代理的方式生成一个具体的实现类

HttpClient HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然JDK 的 java net包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。HttpClient用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议的最新版本。

使用HttpClient发送请求和接收响应一般分为以下几步

(1)创建HttpClient对象;

(2)创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象;

(3)如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HetpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用

setEntity(HttpEntity entity)方法来设置请求参数;

(4)调用HttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个HttpResponse;

(5)调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容,程序可通过该对象获取服务器的响应内容;

(6)释放连接。无论执行方法是否成功,都必须释放连接。

Okhttp

Okhttp作为目前Android使用最为广泛的网络框架之一,是一个高效的HTTP Client

Okhttp原理解析:论文级深度长文 OkHttp 原理解析!_retryonconnectionfailure-CSDN博客

Forest优点

使用 Forest 就像使用类似 Dubbo 那样的 RPC 框架一样,只需要定义接口,调用接口即可,不必关心具体发送 HTTP 请求的细节。同时将 HTTP 请求信息与业务代码解耦,方便您统一管理大量 HTTP 的 URL、Header 等信息。而请求的调用方完全不必在意 HTTP 的具体内容,即使该 HTTP 请求信息发生变更,大多数情况也不需要修改调用发送请求的代码。

Forest使用
依赖
<dependency>
    <groupId>com.dtflys.forest</groupId>
    <artifactId>forest-spring-boot-starter</artifactId>
    <version>1.5.36</version>
</dependency>
配置
# 在spring上下文中bean的id, 默认值为forestConfiguration
forest.bean-id=forestConfiguration
# 后端HTTP API: 默认使用:okhttp3,也可以替换成:httpclient
forest.backend=okhttp3
# 连接池最大连接数,默认值为500
forest.max-connections=1000
# 每个路由的最大连接数,默认值为500
forest.max-route-connections=500
# 请求超时时间,单位为毫秒, 默认值为3000
forest.timeout=3000
# 连接超时时间,单位为毫秒, 默认值为2000
forest.connect-timeout=3000
# 单向验证的HTTPS的默认SSL协议,默认为SSLv3
forest.ssl-protocol=SSLv3
# 打开或关闭日志,默认为true,也可以在单个方法上加注解是否开启:@LogEnabled
forest.logEnabled=true
用法

构建接口:在 Forest 中,所有的 HTTP 请求信息都要绑定到某一个接口的方法上,不需要编写具体的方法体去发送请求。

@BaseRequest(baseURL="http://localhost:8081/development",connectTimeout = 60000,readTimeout = 60000)
public interface ForestClient {

    @Get(url = "/all")
    List<Development> selectAllDepartment(OnError onError);

}

使用:

(1)GET请求
@RestController
@RequestMapping("/forest")
public class ForestController {
    @Resource
    private MyForest myForest;

   @GetMapping("/select")
    public List<Development> selectDepartmentList(){
        return forestClient.selectAllDepartment((forestRuntimeException, forestRequest, forestResponse)->{
            int status = forestResponse.getStatusCode(); // 获取请求响应状态码
            String content = forestResponse.getContent(); // 获取请求的响应内容
            Object result = forestResponse.getResult();// 获取方法返回类型对应的最终数据结果
        });
    }
}
(2)POST方式
@BaseRequest(baseURL="http://localhost:8081/development",connectTimeout = 60000,readTimeout = 60000)
public interface ForestClient {

    /**
     * 通过 @Request 注解的 type 参数指定 HTTP 请求的方式。
     */
    @Request(
            url = "http://localhost:8080/hello",
            type = "POST"
    )
    ForestResponse<String> save(@Body Development development);

    /**
     * 使用 @Post 注解,可以去掉 type = "POST" 这行属性
     */
    @Post("http://localhost:8080/hello")
    ForestResponse<String> save(@Body Development development);

    /**
     * 使用 @PostRequest 注解,和上面 @Post 效果等价
     */
    @PostRequest("http://localhost:8080/hello")
    ForestResponse<String> save(@Body Development development);

}

注意

  • @Get@GetRequest两个注解的效果是等价的,@Post@PostRequest@Put@PutRequest等注解也是同理

(3)@DataVariable注解

整个URL都通过 @DataVariable 注解修饰的参数动态传入 => 相当于@RequestParam注解

@GetMapping("/all")
public List<Development> getAllDevelopment(@RequestParam("myURL") String myURL){
    return forestClient.selectAllDepartment(myURL);
}

@Request("${myURL}")
List<Development> selectAllDepartment(@DataVariable("myURL") String myURL);
/**
 * 通过参数转入的值值作为URL的一部分
 */
@Request("http://${myURL}/abc")
String send2(@DataVariable("myURL") String myURL);
(4) @Header注解

使用 @Header 注解将参数绑定到请求头上@Header 注解的 value 指为请求头的名称,参数值为请求头的值

如:@Header("Accept") String accept将字符串类型参数绑定到请求头 Accept 上

HEAD请求类型没有对应的@Head注解,只有@HeadRequest注解,原因是容易和@Header注解混淆

@GetMapping("/all")
public List<Development> getAllDevelopment(HttpServletRequest httpServletRequest){
    String accept = httpServletRequest.getHeader("Accept");
    String accessToken = httpServletRequest.getHeader("AccessToken");
    return forestClient.selectAllDepartment(accept,accessToken);
}
/**
 * 使用 @Header 注解将参数绑定到请求头上
 * @Header("Accept") String accept将字符串类型参数绑定到请求头 Accept 上 即:Accept = accept.value
 * @Header("AccessToken") String accessToken将字符串类型参数绑定到请求头accessToken上 即:accessToken = accessToken.value
 */
@Request("/all")
List<Development> selectAllDepartment(@Header("Accept") String accept, @Header("accessToken") String accessToken);

/**
 * 使用 @Header 注解可以修饰 Map 类型的参数
 * Map 的 Key 指为请求头的名称,Value 为请求头的值
 * 通过此方式,可以将 Map 中所有的键值对批量地绑定到请求头中
 */
@Post("http://localhost:8080/hello/user?username=foo")
void headHelloUser(@Header Map<String, Object> headerMap);


/**
 * 使用 @Header 注解可以修饰自定义类型的对象参数
 * 依据对象类的 Getter 和 Setter 的规则取出属性
 * 其属性名为 URL 请求头的名称,属性值为请求头的值
 * 以此方式,将一个对象中的所有属性批量地绑定到请求头中
 */
@Post("http://localhost:8080/hello/user?username=foo")
void headHelloUser(@Header MyHeaderInfo headersInfo);
(5)@Body注解

@Body注解:您可以使用@Body注解修饰参数的方式,将传入参数的数据绑定到 HTTP 请求体中。

/**
 * 默认body格式为 application/x-www-form-urlencoded,即以表单形式序列化数据
 */
@Post(
    url = "http://localhost:8080/user",
    headers = {"Accept:text/plain"}
)
ForestResponse<String> save(@Body Development development);

除此之外还可以使用JSON格式,对象等

@Post(value = "/session/check-batch",timeout = 60000)
Result<PickItemsCheckRespDTO> checkPickItemBatch(@Body User user);

请求体:
{
    "id":"123",
    "name":"zhangsan"
}
(6) @BaseRequest注解

@BaseRequest注解定义在接口类上,在@BaseRequest上定义的属性会被分配到该接口中每一个方法上,但方法上定义的请求属性会覆盖@BaseRequest上重复定义的内容。 因此可以认为@BaseRequest上定义的属性内容是所在接口中所有请求的默认属性。

/**
 * @BaseRequest 为配置接口层级请求信息的注解,其属性会成为该接口下所有请求的默认属性,但可以被方法上定义的属性所覆盖
 */
@BaseRequest(
    baseURL = "http://localhost:8080",     // 默认域名
    headers = {
        "Accept:text/plain"                // 默认请求头
    },
    sslProtocol = "TLS"                    // 默认单向SSL协议
)
public interface MyClient {
    // 方法的URL不必再写域名部分
    @Get("/hello/user")     
    String send1(@Query("username") String username);
    // 若方法的URL是完整包含http://开头的,那么会以方法的URL中域名为准,不会被接口层级中的baseURL属性覆盖
    @Get("http://www.xxx.com/hello/user")
    String send2(@Query("username") String username);
    @Get(
        url = "/hello/user",
        headers = {
            "Accept:application/json"      // 覆盖接口层级配置的请求头信息
        }
    )     
    String send3(@Query("username") String username);
}
(7)@Query的请求
public interface MyClient {

    @Request(
            url = "http://localhost:8080/hello/user",
            headers = "Accept: text/plain"
    )
    String sendRequest(@Query("uname") String username);
}

实际的url地址为:http://localhost:8080/hello/user?uname=username

/**
 * 使用 @Query 注解,可以直接将该注解修饰的参数动态绑定到请求url中
 * 注解的 value 值即代表它在url的Query部分的参数名
 */
@Get("http://localhost:8080/abc")
String send(@Query("a") String a, @Query("b") String b);

url:http://localhost:8080/abc?a=a&b=b


/**
 * 使用 @Query 注解,可以修饰 Map 类型的参数
 * 很自然的,Map 的 Key 将作为 URL 的参数名, Value 将作为 URL 的参数值
 * 这时候 @Query 注解不定义名称
 */
@Get("http://localhost:8080/abc")
String send1(@Query Map<String, Object> map);


/**
 * @Query 注解也可以修饰自定义类型的对象参数
 * 依据对象类的 Getter 和 Setter 的规则取出属性
 * 其属性名为 URL 参数名,属性值为 URL 参数值
 * 这时候 @Query 注解不定义名称
 */
@Get("http://localhost:8080/abc")
String send2(@Query UserInfo user);

则对应的url是:http://localhost:8080/abc?name=zhangsan&password=123456

数据转换
(1)序列化

Forest中对数据进行序列化可以通过指定contentType属性或Content-Type头指定内容格式。

@Request(
        url = "http://localhost:8080/hello/user",
        type = "post",
        contentType = "application/json"    // 指定contentType为application/json
)
String postJson(@Body MyUser user);   // 自动将user对象序列化为JSON格式

同理,指定为application/xml会将参数序列化为XML格式,text/plain则为文本,默认的application/x-www-form-urlencoded则为表格格式。

(2)反序列化

HTTP请求响应后返回结果的数据同样需要转换,Forest则会将返回结果自动转换为您通过方法返回类型指定对象类型。这个过程就是反序列化,您可以通过dataType指定返回数据的反序列化格式。

@Request(
    url = "http://localhost:8080/data",
    dataType = "json"        // 指定dataType为json,将按JSON格式反序列化数据
)
Map getData();               // 请求响应的结果将被转换为Map类型对象
Forest进阶
(1)单向认证

如果访问的目标站点的SSL证书由信任的Root CA发布的,无需做任何事情便可以自动信任

public interface Gitee {
    @Request(url = "https://gitee.com")
    String index();
}

Forest的单向验证的默认协议为SSLv3,如果一些站点的API不支持该协议,可以在全局配置中将ssl-protocol属性修改为其它协议,如:TLSv1.1, TLSv1.2, SSLv2等等。

forest.ssl-protocol=TLSv1.2

全局配置可以配置一个全局统一的SSL协议,但现实情况是有很多不同服务(尤其是第三方)的API会使用不同的SSL协议,这种情况需要针对不同的接口设置不同的SSL协议。

/**
 * 在某个请求接口上通过 sslProtocol 属性设置单向SSL协议
 */
@Get(
    url = "https://localhost:5555/hello/user",
    sslProtocol = "SSL"
)
ForestResponse<String> truestSSLGet();
(2)双向认证

若是需要在Forest中进行双向验证的HTTPS请求,处理如下:

在全局配置中添加keystore配置:

forest:
 ...
 ssl-key-stores:
   - id: keystore1           # id为该keystore的名称,必填
     file: test.keystore     # 公钥文件地址
     keystore-pass: 123456   # keystore秘钥
     cert-pass: 123456       # cert秘钥
     protocols: SSLv3        # SSL协议

接着,在@Request中引入该keystoreid即可

@Request(
    url = "https://localhost:5555/hello/user",
    keyStore = "keystore1"
)
String send();

也可以在全局配置中配多个keystore

forest:
  ...
  ssl-key-stores:
    - id: keystore1          # 第一个keystore
      file: test1.keystore    
      keystore-pass: 123456  
      cert-pass: 123456      
      protocols: SSLv3       

    - id: keystore2          # 第二个keystore
      file: test2.keystore    
      keystore-pass: abcdef  
      cert-pass: abcdef      
      protocols: SSLv3       
      ...
异常处理

发送HTTP请求不会总是成功的,总会有失败的情况。Forest提供多种异常处理的方法来处理请求失败的过程

(1)try-catch方式
@Autowrite
private MyClient myClient;
/**
 * try-catch方式:捕获ForestNetworkException异常类的对象
 */
try {
    String result = myClient.send();
} catch (ForestNetworkException ex) {
    int status = ex.getStatusCode(); 				// 获取请求响应状态码
    ForestResponse response = ex.getResponse(); 	// 获取Response对象
    String content = response.getContent(); 		// 获取请求的响应内容
    String resResult = response.getResult(); 		// 获取方法返回类型对应的最终数据结果
}
(2)回调函数方式
/**
 * 在请求接口中定义OnError回调函数类型参数
 */
@Request(
        url = "http://localhost:8080/hello/user",
        headers = {"Accept:text/plain"},
        data = "username=${username}"
)
String send(@DataVariable("username") String username, OnError onError);

调用的代码如下

// 在调用接口时,在Lambda中处理错误结果
myClient.send("foo",  (exception, request, response) -> {
    int status = response.getStatusCode(); // 获取请求响应状态码
    String content = response.getContent(); // 获取请求的响应内容
    String result = response.getResult(); // 获取方法返回类型对应的最终数据结果
});
(3)ForestResponse

第三种,用ForestResponse类作为请求方法的返回值类型,示例代码如下:

/**
 * 用`ForestResponse`类作为请求方法的返回值类型, 其泛型参数代表实际返回数据的类型
 */
@Request(
        url = "http://localhost:8080/hello/user",
        headers = {"Accept:text/plain"},
        data = "username=${username}"
)
ForestResponse<String> send(@DataVariable("username") String username);

调用和处理的过程如下

ForestResponse<String> response = myClient.send("foo");
// 用isError方法判断请求是否失败, 比如404, 500等情况
if (response.isError()) {
    int status = response.getStatusCode(); // 获取请求响应状态码
    String content = response.getContent(); // 获取请求的响应内容
    String result = response.getResult(); // 获取方法返回类型对应的最终数据结果
}
文件上传
(1)上传:@DataFile注解
/**
 * 用@DataFile注解修饰要上传的参数对象
 * OnProgress参数为监听上传进度的回调函数
 */
@Post(url = "/upload")
Map upload(@DataFile("file") String filePath, OnProgress onProgress);

调用上传接口以及监听上传进度的代码如下:

Map result = myClient.upload("D:\\TestUpload\\xxx.jpg", progress -> {
    System.out.println("total bytes: " + progress.getTotalBytes());   // 文件大小
    System.out.println("current bytes: " + progress.getCurrentBytes());   // 已上传字节数
    System.out.println("progress: " + Math.round(progress.getRate() * 100) + "%");  // 已上传百分比
    if (progress.isDone()) {   // 是否上传完成
        System.out.println("--------   Upload Completed!   --------");
    }
});

在文件上传的接口定义中,除了可以使用字符串表示文件路径外,还可以用以下几种类型的对象表示要上传的文件:

/**
 * File类型对象
 */
@Post(url = "/upload")
Map upload(@DataFile("file") File file, OnProgress onProgress);

/**
 * byte数组
 * 使用byte数组和Inputstream对象时一定要定义fileName属性
 * "${1}"表示将方法的第二个参数filename的值替换到注解参数中的占位符"${1}"的位置上
 */
@Post(url = "/upload")
Map upload(@DataFile(value = "file", fileName = "${1}") byte[] bytes, String filename);

/**
 * Inputstream 对象
 * 使用byte数组和Inputstream对象时一定要定义fileName属性
 */
@Post(url = "/upload")
Map upload(@DataFile(value = "file", fileName = "${1}") InputStream in, String filename);

/**
 * Spring Web MVC 中的 MultipartFile 对象
 */
@PostRequest(url = "/upload")
Map upload(@DataFile(value = "file") MultipartFile multipartFile, OnProgress onProgress);

/**
 * Spring 的 Resource 对象
 */
@Post(url = "/upload")
Map upload(@DataFile(value = "file") Resource resource);
(2)多文件上传
/**
 * 上传Map包装的文件列表
 * 其中 ${_key} 代表Map中每一次迭代中的键值
 */
@PostRequest(url = "/upload")
ForestRequest<Map> uploadByteArrayMap(@DataFile(value = "file", fileName = "${_key}") Map<String, byte[]> byteArrayMap);

/**
 * 上传List包装的文件列表
 * 其中 ${_index} 代表每次迭代List的循环计数(从零开始计)
 */
@PostRequest(url = "/upload")
ForestRequest<Map> uploadByteArrayList(@DataFile(value = "file", fileName = "test-img-${_index}.jpg") List<byte[]> byteArrayList);

/**
 * 上传数组包装的文件列表
 * 其中 ${_index} 代表每次迭代List的循环计数(从零开始计)
 */
@PostRequest(url = "/upload")
ForestRequest<Map> uploadByteArrayArray(@DataFile(value = "file", fileName = "test-img-${_index}.jpg") byte[][] byteArrayArray);
(3)下载
/**
 * 在方法上加上@DownloadFile注解
 * dir属性表示文件下载到哪个目录
 * filename属性表示文件下载成功后以什么名字保存,如果不填,这默认从URL中取得文件名
 * OnProgress参数为监听上传进度的回调函数
 */
@Get(url = "http://localhost:8888/123.png")
//dir = "${0}"表示将方法的第一个参数dir的值替换到注解参数中的占位符"${0}"的位置上,
// 而filename = "${1}"表示将方法的第二个参数filename的值替换到注解参数中的占位符"${1}"的位置上
@DownloadFile(dir = "${0}", filename = "${1}")
File downloadFile(String dir, String filename, OnProgress onProgress);

调用下载接口以及监听上传进度的代码如下:

@Autowired
private ApiTestClient apiTestClient;

@GetMapping("/download")
public File download(){
    OnProgress onProgress = progress -> {
        System.out.println("total bytes: " + progress.getTotalBytes());   // 文件大小
        System.out.println("current bytes: " + progress.getCurrentBytes());   // 已下载字节数
        System.out.println("progress: " + Math.round(progress.getRate() * 100) + "%");  // 已下载百分比
        if (progress.isDone()) {   // 是否下载完成
            System.out.println("--------   Download Completed!   --------");
        }
    };
    //获取mac根目录
    String downLoadFile =  System.getProperty("user.home") + "/Desktop/";
    return apiTestClient.downloadFile(downLoadFile,"123.png",onProgress);
}

如果您不想将文件下载到硬盘上,而是直接在内存中读取,可以去掉@DownloadFile注解,并且用以下几种方式定义接口

/**
 * 返回类型用byte[],可将下载的文件转换成字节数组
 */
@GetRequest(url = "http://localhost:8080/images/test-img.jpg")
byte[] downloadImageToByteArray();

/**
 * 返回类型用InputStream,用流的方式读取文件内容
 */
@GetRequest(url = "http://localhost:8080/images/test-img.jpg")
InputStream downloadImageToInputStream();
Forest和Feign的区别?

Feign和Forest都是基于注解的声明式HTTP框架,但封装程度和api接口风格不太相同

比如:Feign没有封装SSL证书验证、连接池、正向代理等功能,需要用于手动写代码提供OkHttp或Httpclient等框架的Client对象,而Forest是尽可能封装完整,以形成统一的屏蔽层,所以不要改代码就可以通过配置或注解随意切换OkHttp或Httpclient的底层HTTP框架

轻量级HTTP客户端框架-RestTemplate

1定义

使用Springboot提供的RestTemplateBuilder构造类来构造一个RestTemplate,自定义一些连接参数

/**
 * 使用Springboot提供的RestTemplateBuilder构造类来构造一个RestTemplate,自定义一些连接参数
 * */
@Configuration
public class WebConfiguration {
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder){
        return builder
                //设置连接超时时间(Duration.ofSeconds(5000) 创建了5000秒的持续时间对象)
                .setConnectTimeout(Duration.ofSeconds(5000))
                //设置读取超时时间
                .setReadTimeout(Duration.ofSeconds(5000))
                //设置认证信息
                .basicAuthentication("username","password")
                //设置根路径
                .rootUri("http://localhost:8081/provider/depart")
                //构建
                .build();
    }
}
2 RestTemplate API使用
(1) GET

发送GET请求的方法有两种

  • public <T> T getForObject(...)

  • public <T> ResponseEntity<T> getForEntity(...)

getForEntity() 后缀带有Entity的方法都代表返回一个ResponseEntity<T>,ResponseEntity<T>是Spring对HTTP请求响应的封装,包括了几个重要的元素,如响应码,contentType、contentLength、响应消息体等

@AutoWrite
private RestTemplate restTemplate;

@GetMapping("/select/{id}")
public ResponseEntity<Department> getUserInfo(@PathVariable String id) {
  String url = "/select/"+id;
  //发送请求
  ResponseEntity<Department> forEntity = restTemplate.getForEntity(url, Department.class);
  //响应
  System.out.println("状态码:"+forEntity.getStatusCode());
  System.out.println("状态码内容:"+forEntity.getStatusCodeValue());
  HttpHeaders headers = forEntity.getHeaders();
  System.out.println("响应头:"+headers);
  Object body = forEntity.getBody();
  System.out.println("响应内容:"+body);
}

getForObject()

相比于前者getForEntity()该方法则是,更偏向于直接获取响应内容的,因为他直接返回响应实体的body(响应内容)

(2)POST

POST请求有如下三种方法

  • public URI postForLocation(...)

  • public <T> T postForObject(...)

  • public <T> ResponseEntity<T> postForEntity(...)

postForEntity():该方法有三个参数

第一个为调用服务的地址(URL)

第二个参数表示上传的参数(json格式提交)

第三个表示返回响应内容的具体类型

postForObject()

使用方法与postForEntity()类似只是多了一个传入对象参数(传入方式与postForEntity()相同)

@GetMapping("/select/list")
public List<Department> selectDepartmentList() {
  String url = "/select/list";
  List forObject = restTemplate.getForObject(url, List.class);
  return (List<Department>)forObject;
}

postForLocation()

postForLocation传参用法与前两者一致,只不过返回从实体变成了一个URL,因此它不需要指定返回响应内容的类型。

 URI uri = restTemplate.postForLocation("https://httpbin.org/post", user);
(3)使用POST以表单方式提交

自己封装一个请求体,需要用到如下几个类

  • HttpHeaders

  • MultiValueMap<K,V>

  • HttpEntity<T>

HttpHeaders

故名思意,就是用来封装Http请求的请求头的,这里我们要设置他的ContentTypeMediaType.APPLICATION_FORM_URLENCODED以使得我们提交的参数是以Form(表单)的形式提交。

MultiValueMap<K,V>

该类是用来封装请求参数的,是以key-value的形式封装但是以单个key对应多个value的格式传输(也就是是以单个key:[value...]的格式传输的)。

HttpEntity<T>

该类是用来封装请求的,主要作用就是将请求头和请求体封装在一起成为一个请求实体 T用来指定用来封装参数的容器的类型。

@Test
void contextLoads() {
        //请求地址
        String url = "https://httpbin.org/post";
        //设置请求头, x-www-form-urlencoded格式的数据
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        //提交参数设置
        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        map.add("name","鲁大师");
        //组装请求体
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, httpHeaders);
  
        //发送post请求并打印结果 以String类型接收响应结果JSON字符串
        String s = restTemplate.postForObject(url, request, String.class);
        System.out.println(s);
    }
(5)PUT()

使用方法与postForEntity()参数基本一致,只是put方法没有返回值(也就不必去设置响应内容的类型了)。

(6)delete()

delete()可以指定url中的中的参数,但是RestTemplatedelete()方法是不支持上传requestBody的。

**(7)optionsForAllow(url)

该方法的主要用来判断该服务地址,能够使用那种方法去执行

@Test
void contextLoads() {
  //请求地址
  String url = "http://httpbin.org/get";
  Set<HttpMethod> httpMethods = restTemplate.optionsForAllow(url);
  System.out.println(httpMethods);
}
(8)exchange()
  • <T> ResponseEntity<T> exchange()

该方法允许用户指定请求的方法(get,post,put等) 可以在请求中增加body以及头信息,其内容通过参数HttpEntity<?> requestEntity描述 exchange支持’含参数的类型(即泛型)'作为返回类型,该特性通过ParameterizedTypeReferenceresponseType 描述

该方法支持五个参数

  • 第一个是服务地址

  • 第二个是请求方法

  • 第三个是写入的请求实体

  • 第四个是响应内容的类型

  • 第五个是扩展模板的变量或包含URI模板变量的映射

@Test
void contextLoads() {
    //请求地址
    String url = "http://httpbin.org/post";
    User user = new User();
    user.setName("彭于晏");
    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.setContentType(MediaType.APPLICATION_JSON);
    HttpEntity<User> userHttpEntity = new HttpEntity<>(user, httpHeaders);
  
    ResponseEntity<Object> exchange = restTemplate.exchange(url, HttpMethod.POST, userHttpEntity, Object.class);
    System.out.println(exchange);
}
(9)EXECUTE
  • <T> T execute()

该方法就是执行请求的方法,我们可以发现上述的所有方法的最后执行都是调用的该方法执行,所以他在RestTemplate中十分重要

该方法有五个参数

  • 服务地址

  • 请求的方法

  • 准备请求的对象(requestCallback

  • 从响应中提取返回值的对象

  • 扩展模板的变量或包含URI模板变量的映射

@Override
	@Nullable
	public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
			@Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {

		URI expanded = getUriTemplateHandler().expand(url, uriVariables);
		return doExecute(expanded, method, requestCallback, responseExtractor);
	}

占位符
  • 当我们传入简单的对象如String,Integer时且路径中有嵌入的占位符时就会代替调用URL中占位符

@Test
void contextLoads() {
    //请求地址
    String url = "http://httpbin.org/{2}/get";
    HashMap<String, String> map = new HashMap<>();
    Object forObject = restTemplate.getForObject(url, Object.class, 99);
    System.out.println(forObject);
}
===>
  http://httpbin.org/99/get

  • 18
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值