Spring Cloud从入门到放弃(三):万字讲解HTTP利器--RestTemplate

前言

Spring Cloud系列: 点击查看Spring Cloud系列文章


RestTemplate

RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。

访问restful服务,传统的还有Apache的HttpClient。不过相比之下,RestTemplate使用起来更加方便简洁。


RestTemplate常用方法

RestTemplate对不同的HTTP方法,有不同的请求方法,如下图所示

在这里插入图片描述
注:exchange和excute可以通用上述HTTP方法,即GET方法可以使用,POST方法也可以使用等

RestTemplate方法的名称遵循命名约定,第一部分指示正在调用的HTTP方法,第二部分指示返回的内容。例如,该方法getForObject()将执行GET,将HTTP响应转换为您选择的对象类型,然后返回该对象。该方法postForLocation() 将执行POST,将给定的对象转换为HTTP请求,然后返回响应HTTP Location标头,可以在其中找到新创建的对象。

ForEntity方法的返回值是一个ResponseEntity,ResponseEntity是Spring对HTTP请求响应的封装,包括了几个重要的元素,如响应码、响应头、contentType、contentLength、响应消息体等。

ForObject方法实际上是对ForEntity函数的进一步封装,ForObject方法会将响应体转换成你想要的对象类型,如果你只关注返回的消息体的内容,对其他信息都不关注,此时可以使用ForObject。

如下:

//第一个参数是url,第二个是请求参数,第三个是返回结果转换的类型,第四个是uri参数(即uri上的参数)
public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType,
			Object... uriVariables) throws RestClientException 


例:

restTemplate.postForObject(
        "http://example.com/hotels/{hotel}/bookings/{booking}", String.class,"42", "21");

HttpMessageConverter

RestTemplate默认使用HttpMessageConverter实例将HTTP消息转换成POJO或者从POJO转换成HTTP消息。默认情况下会注册主mime类型的转换器,但是您也可以编写自己的转换器并通过messageConverters()属性进行注入 。在ForObject方法的responseType 参数,它让你传入一个响应体的对象类型,底层就是用HttpMessageConverter将其做映射。

自定义HttpMessageConverter,实现AbstractHttpMessageConverter接口即可

如下

public class StringToJsonObjectMessageConverter extends AbstractHttpMessageConverter<JSONObject> {

    /**
     * HttpMessageConvert是否处理接受的这个类
     */
    @Override
    protected boolean supports(Class<?> clazz) {
        return true;
    }

    /**
     *对数据进行处理并返回
     */
    @Override
    protected JSONObject readInternal(Class<? extends JSONObject> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }

    /**
     *处理如何输出数据到response
     */
    @Override
    protected void writeInternal(JSONObject jsonObject, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {

    }
}

2、在restTemplate的MessageConverters属性set进自定义的转换器

@Bean
public RestTemplate restTemplate() {
    HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();  
    RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
    // 设置自定义转换器,第一个参数为转换器的顺序,越小越大,最小为0。add和set方法都可以
    restTemplate.getMessageConverters().set(1,new StringToJsonObjectMessageConverter());
    return restTemplate;
}

RestTemplate的一些内置转换器

StringHttpMessageConverter
StringHttpMessageConverter可以从HTTP请求和响应中读取和写入字符串的实现。默认情况下,此转换器支持所有文字媒体类型( text/*),写入类型为Content-Type的text/plain。

FormHttpMessageConverter
FormHttpMessageConverter可以从HTTP请求和响应中读取和写入表单数据的实现。默认情况下,此转换器读取和写入媒体类型 application/x-www-form-urlencoded。表单数据可从中读取或写入 MultiValueMap<String, String>。

ByteArrayHttpMessageConverter
ByteArrayHttpMessageConverter可以从HTTP请求和响应读取和写入字节数组的实现。默认情况下,该转换器支持所有介质类型(/),写入类型为Content-Type的application/octet-stream。可以通过设置supportedMediaTypes属性和overriding来覆盖它getContentType(byte[])。


创建RestTemplate

创建RestTemplate很简单,只需要new RestTemplate(),如果使用Spring架构,只需要将创建的RestTemplate实例通过XML或注解的方式注册到Spring容器中即可

如:

@Bean
public RestTemplate restTemplate() {
    return new RestTemplate();
}

RestTemplate 有三种构造函数,一个无参,两个有参,如下

public RestTemplate() {}

public RestTemplate(ClientHttpRequestFactory requestFactory) {}

public RestTemplate(List<HttpMessageConverter<?>> messageConverters) {}

创建多个RestTemplate实例

有时候,我们需要两个或者多个不同配置的RestTemplate,那我们可以在配置文件注册多个RestTemplate 的bean

@Configuration
public class MyConfiguration {

    @LoadBalanced
    @Bean
    RestTemplate loadBalanced() {
        return new RestTemplate();
    }

    @Primary
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

在注入的时候,使用 @Qualifier(“名称”)进行注入,否则会报错

如:

    @Autowired
    @Qualifier("loadBalanced")
    private RestTemplate loadBalanced;

RestTemplate 的负载均衡

RestTemplate有客户端负载均衡功能,那怎么开启 呢?很简单,只需要在RestTemplate的bean上添加@LoadBalanced注解即可

如:

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
    return new RestTemplate();
}

注:RestTemplate 是通过拦截器改变请求的URI的方式来指定服务器的,请求的url需要通过服务名调用而不是服务地址,如果写成服务地址就没法实现客户端负载均衡了(在有多个服务提供实例,才有必要用负载均衡)

例:

restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class);

设置超时时间

设置超时时间,可以直接使用Spring的底层基于HttpClient的HttpComponentsClientHttpRequestFactory,此处设置的是ClientHttpRequestFactory级别的全局超时时间

  @Bean
    public RestTemplate restTemplate() {
        HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        //从连接池获取连接的超时时间
        httpRequestFactory.setConnectionRequestTimeout(30 * 1000);
        //客户端和服务器建立连接的超时时间
        httpRequestFactory.setConnectTimeout(30 * 3000);
        //读取数据的超时时间
        httpRequestFactory.setReadTimeout(30 * 3000);
        RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
        return restTemplate;
    }

创建URI对象

在RestTemplate调用HTTTP服务时,URL可以是String类型 或 URI类型,下面我们就来看一下URI对象如何创建

URI对象的无参构造方法是私有的,不能直接调用。需要使用它的有参构造方法

 public URI(String str) throws URISyntaxException {

    }

    public URI(String scheme, String userInfo, String host, int port, String path, String query, String fragment) throws URISyntaxException {
    
    }

除了使用构造方法,我们还可以通过UriComponentsBuilder构造器来生成URI对象

例:

UriComponents uriComponents = UriComponentsBuilder.fromUriString(
        "http://example.com/hotels/{hotel}/bookings/{booking}").build()
        .expand("42", "21")//传递uri参数,无参不需要
        .encode();

URI uri = uriComponents.toUri();


RestTemplate API使用

getForEntity():返回ResponseEntity对象

/**
 * 参数url: String类型 或 URI类型的请求地址
 * 参数responseType: 指定返回的实体类型,class对象
 * 参数uriVariables: uri参数,可以是变长数组或map
 * 返回值:responseType指定的Object类型
 */
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables){}
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables){}
public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType){}

例:

ResponseEntity<Book> responseEntity = restTemplate.getForEntity("http://127.0.0.1:8080/getbook?bookname={1}", Book.class, "java");
Book book = responseEntity.getBody();  //响应体转换为Book类型
int statusCodeValue = responseEntity.getStatusCodeValue();  //响应状态码
HttpHeaders headers = responseEntity.getHeaders();  //响应头信息

getForObject():返回指定的Object类型

public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables){}
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
public <T> T getForObject(URI url, Class<T> responseType)

例:

Book book = restTemplate.getForObject("http://127.0.0.1:8080/getbook", Book.class);

POST方法

postForEntity方法

/**
 * 参数url: String类型 或 URI类型的请求地址
 * 参数request: 请求body,可以是HttpEntity类型(可设置request header),或其它Object类型
 * 参数responseType: 指定返回的实体类型,class对象
 * 参数uriVariables: uri参数,可以是变长数组或map
 * 返回值:ResponseEntity<T>是Spring对HTTP响应的封装,包括了几个重要的元素,如响应码、contentType、contentLength、response header信息,response body信息等
 */
@Override
public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)  throws RestClientException {
}

@Override
public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)  throws RestClientException {
}

@Override
public <T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException {
}

例:以下是使用HttpEntity类型请求参数的例子

        //使用HttpEntity 类型的请求参数发送请求,HttpEntity 比一般的Object参数好的地方是,我们可以在HttpEntity 设置一些请求头信息
        String url="http://xxx";
        //请求头
        HttpHeaders httpHeaders=new HttpHeaders();
        //设置Content-Type(用于指示资源的MIME类型 media type)在请求中 (如POST 或 PUT),客户端告诉服务器实际发送的数据类型。在响应中,Content-Type标头告诉客户端实际返回的内容的内容类型。
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        //自定义参数
        httpHeaders.add("myKey","myValue");
        MultiValueMap<String,String>multiValueMap=new LinkedMultiValueMap<>();
        multiValueMap.add("name","m");
        HttpEntity httpEntity=new HttpEntity(multiValueMap,httpHeaders);
        ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity(url, httpEntity, String.class);
        //获取请求头
        HttpHeaders headers = stringResponseEntity.getHeaders();
        //取出请求头的参数,getFirst为获取第一个,get为获取所有
        String myKey = headers.getFirst("myKey");

        HttpStatus statusCode = stringResponseEntity.getStatusCode();
        //响应体自动转换成response类型
        String body = stringResponseEntity.getBody();

注意,构造HttpEntity 的body使用MultiValueMap,HttpEntity接受的request类型就是它。

MultiValueMap是Map的一个子类,它的一个key可以存储多个value,因为它的value是一个list集合

public interface MultiValueMap<K, V> extends Map<K, List<V>> {}

restTemplate.postForEntity方法虽然表面上接收的request是@Nullable Object request类型,但是你追踪下去会发现,这个request是用HttpEntity来解析。代码如下

public HttpEntityRequestCallback(@Nullable Object requestBody, @Nullable Type responseType) {
	super(responseType);
	if (requestBody instanceof HttpEntity) {
		this.requestEntity = (HttpEntity<?>) requestBody;
	}
	else if (requestBody != null) {
		this.requestEntity = new HttpEntity<>(requestBody);
	}
	else {
		this.requestEntity = HttpEntity.EMPTY;
	}
}

以下为使用对象做请求参数的例子

//直接使用对象作为request参数
 restTemplate.postForEntity(url,new RequestPojo(),String.class);

postForObject()

参数一样,只是返回的类型不一样,postForObject直接返回响应体,并转换成responseType

@Override
public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
        throws RestClientException {
}

@Override
public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
        throws RestClientException {
}

@Override
public <T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException {
}

例:

//直接返回了responseType类型的结果,没有请求头等信息
String result = restTemplate.postForObject(url, new RequestPojo(), String.class);

exchange方法

exchange()方法跟上面的getForObject()、getForEntity()、postForObject()、postForEntity()等方法不同之处在于它可以指定请求的HTTP类型。

    @Override
	public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
			@Nullable HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables)
			throws RestClientException {
	}

	@Override
	public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
			@Nullable HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables)
			throws RestClientException {
	}

	@Override
	public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
			Class<T> responseType) throws RestClientException {
	}


参数说明:url为请求地址,可以是字符串,也可以是URI对象。method为请求的方法类型,如:HttpMethod.GET表示GET方法,requestEntity表示一个HttpEntity类型的请求对象,responseType为返回的响应类型,uriVariables为URI参数

例:

HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("MyRequestHeader", "MyValue");
HttpEntity<?> requestEntity = new HttpEntity(requestHeaders);

HttpEntity<String> response = template.exchange(
        "http://example.com/hotels/{hotel}",
        HttpMethod.GET, requestEntity, String.class, "42");

String responseHeader = response.getHeaders().getFirst("MyResponseHeader");
String body = response.getBody();
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值