Skip to main content
 首页 » 编程设计

RestTemplate简明教程

2022年07月19日121jillzhang

RestTemplate简明教程

本文我们学习Spring REST 客户端 – RestTemplate,包括其各种操作如何使用。

1. 使用GET方法获取资源

示例中使用到的Foo实体类定义:

@Data 
public class Foo { 
    private long id; 
    private String name; 
 
    public Foo() { 
        super(); 
    } 
 
    public Foo(final String name) { 
        super(); 
 
        this.name = name; 
    } 
 
    public Foo(final long id, final String name) { 
        super(); 
 
        this.id = id; 
        this.name = name; 
    } 
} 

测试的服务器端rest 接口代码:

import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic; 
 
import java.net.URI; 
import java.util.Collection; 
import java.util.Map; 
 
import org.springframework.http.HttpHeaders; 
import org.springframework.http.HttpStatus; 
import org.springframework.http.ResponseEntity; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.PathVariable; 
import org.springframework.web.bind.annotation.RequestBody; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod; 
import org.springframework.web.bind.annotation.RequestParam; 
import org.springframework.web.bind.annotation.ResponseBody; 
import org.springframework.web.bind.annotation.ResponseStatus; 
import org.springframework.web.client.HttpClientErrorException; 
import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 
 
import com.google.common.collect.ImmutableMap; 
import com.google.common.collect.Maps; 
 
@Controller 
public class FooController { 
     
    private Map<Long, Foo> fooRepository = Maps.newHashMap(ImmutableMap.of(1L, new Foo(1L, randomAlphabetic(4)))); 
 
    public FooController() { 
        super(); 
    } 
 
    @RequestMapping(method = RequestMethod.GET, value = "/foos") 
    @ResponseBody 
    public Collection<Foo> findListOfFoo() { 
        return fooRepository.values(); 
    } 
 
    // API - read 
 
    @RequestMapping(method = RequestMethod.GET, value = "/foos/{id}") 
    @ResponseBody 
    public Foo findById(@PathVariable final long id) throws HttpClientErrorException { 
        Foo foo = fooRepository.get(id); 
         
        if (foo == null) { 
            throw new HttpClientErrorException(HttpStatus.NOT_FOUND); 
        } else { 
            return foo; 
        }     
    } 
 
    // API - write 
 
    @RequestMapping(method = RequestMethod.PUT, value = "/foos/{id}") 
    @ResponseStatus(HttpStatus.OK) 
    @ResponseBody 
    public Foo updateFoo(@PathVariable("id") final long id, @RequestBody final Foo foo) { 
        fooRepository.put(id, foo); 
        return foo; 
    } 
     
    @RequestMapping(method = RequestMethod.PATCH, value = "/foos/{id}") 
    @ResponseStatus(HttpStatus.OK) 
    @ResponseBody 
    public Foo patchFoo(@PathVariable("id") final long id, @RequestBody final Foo foo) { 
        fooRepository.put(id, foo); 
        return foo; 
    } 
     
    @RequestMapping(method = RequestMethod.POST, value = "/foos") 
    @ResponseStatus(HttpStatus.CREATED) 
    @ResponseBody 
    public ResponseEntity<Foo> postFoo(@RequestBody final Foo foo) { 
         
        fooRepository.put(foo.getId(), foo); 
        final URI location = ServletUriComponentsBuilder 
                .fromCurrentServletMapping() 
                .path("/foos/{id}") 
                .build() 
                .expand(foo.getId()) 
                .toUri(); 
         
        final HttpHeaders headers = new HttpHeaders(); 
        headers.setLocation(location); 
         
        final ResponseEntity<Foo> entity = new ResponseEntity<Foo>(foo, headers, HttpStatus.CREATED); 
        return entity; 
    } 
     
    @RequestMapping(method = RequestMethod.HEAD, value = "/foos") 
    @ResponseStatus(HttpStatus.OK) 
    @ResponseBody 
    public Foo headFoo() { 
        return new Foo(1, randomAlphabetic(4)); 
    } 
 
    @RequestMapping(method = RequestMethod.POST, value = "/foos/new") 
    @ResponseStatus(HttpStatus.CREATED) 
    @ResponseBody 
    public Foo createFoo(@RequestBody final Foo foo) { 
        fooRepository.put(foo.getId(), foo); 
        return foo; 
    } 
 
    @RequestMapping(method = RequestMethod.DELETE, value = "/foos/{id}") 
    @ResponseStatus(HttpStatus.OK) 
    @ResponseBody 
    public long deleteFoo(@PathVariable final long id) { 
        fooRepository.remove(id); 
        return id; 
    } 
 
    @RequestMapping(method = RequestMethod.POST, value = "/foos/form") 
    @ResponseStatus(HttpStatus.CREATED) 
    @ResponseBody 
    public String submitFoo(@RequestParam("id") String id) { 
        return id; 
    } 
 
} 

1.1. 获取json

首先从简单的GET请求开始————使用getForEntity()方法的示例:

RestTemplate restTemplate = new RestTemplate(); 
String fooResourceUrl = "http://localhost:8080/spring-rest/foos"; 
ResponseEntity<String> response = restTemplate.getForEntity(fooResourceUrl + "/1", String.class); 
assertThat(response.getStatusCode(), equalTo(HttpStatus.OK)); 

Http 响应也是可以访问,和检查状态码一样,也可以检查响应的内容是否正确,即检查响应体:

ObjectMapper mapper = new ObjectMapper(); 
JsonNode root = mapper.readTree(response.getBody()); 
JsonNode name = root.path("name"); 
assertThat(name.asText(), notNullValue()); 

上面验证响应体中的字符串类型,使用Jackson提供的JsonNode验证一些细节。

1.2. 获取POJO

可以直接把响应体映射值POJO,一般为DTO类。现在可以调用getForObject方法:

Foo foo = restTemplate.getForObject(fooResourceUrl + "/1", Foo.class); 
assertThat(foo.getName(), notNullValue()); 
assertThat(foo.getId(), is(1L)); 

2. 使用HEAD获取请求头

现在看下使用HEAD请求获取请求头,使用headForHeaders方法:

HttpHeaders httpHeaders = restTemplate.headForHeaders(fooResourceUrl); 
assertTrue(httpHeaders.getContentType().includes(MediaType.APPLICATION_JSON)); 

3. 使用POST创建资源

创建资源可以使用三个API ———— postForLocation(), postForObject() , postForEntity()。
第一个返回新创建资源的uri,第二个返回资源本身。

3.1. postForObject API

RestTemplate restTemplate = new RestTemplate(); 
  
HttpEntity<Foo> request = new HttpEntity<>(new Foo("bar")); 
Foo foo = restTemplate.postForObject(fooResourceUrl, request, Foo.class); 
assertThat(foo, notNullValue()); 
assertThat(foo.getName(), is("bar")); 

3.2. postForLocation API

与上面方法类似,postForLocation返回并返回资源,而是返回新增资源的uri:

HttpEntity<Foo> request = new HttpEntity<>(new Foo("bar")); 
URI location = restTemplate.postForLocation(fooResourceUrl, request); 
assertThat(location, notNullValue()); 

4.3. exchange API

下面看看如何使用更通过 exchange API:

RestTemplate restTemplate = new RestTemplate(); 
HttpEntity<Foo> request = new HttpEntity<>(new Foo("bar")); 
ResponseEntity<Foo> response = restTemplate.exchange(fooResourceUrl, HttpMethod.POST, request, Foo.class); 
   
assertThat(response.getStatusCode(), is(HttpStatus.CREATED)); 
   
Foo foo = response.getBody(); 
   
assertThat(foo, notNullValue()); 
assertThat(foo.getName(), is("bar")); 

3.4. 提交表单数据

接下来,看看如何提交表单数据,首先我们需要设置请求头的属性Content-Type” 为application/x-www-form-urlencoded。

HttpHeaders headers = new HttpHeaders(); 
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); 

为了确保大查询串能够发送到服务器端,包括键值对需要使用‘&’符合分隔,可以使用LinkedMultiValueMap包装变量:

MultiValueMap<String, String> map= new LinkedMultiValueMap<>(); 
map.add("id", "1"); 

然后通过 HttpEntity 构建请求内容:

HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers); 

最后,我们使用restTemplate.postForEntity方法连接REST服务接口:

ResponseEntity<String> response = restTemplate.postForEntity( 
  fooResourceUrl+"/form", request , String.class); 
   
assertThat(response.getStatusCode(), is(HttpStatus.CREATED)); 

4. 使用OPTIONS获取所有支持的操作

接下来看使用OPTIONS获取特定URI所支持的操作,使用optionsForAllow:

Set<HttpMethod> optionsForAllow = restTemplate.optionsForAllow(fooResourceUrl); 
HttpMethod[] supportedMethods 
  = {HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE}; 
assertTrue(optionsForAllow.containsAll(Arrays.asList(supportedMethods))); 

5. 使用PUT更新资源

put方法非常简单。我们通过更通用的exchange实现put功能。

5.1. 使用exchange实现简单put操作

首先开始简单的put操作,该操作没有返回任何请求体给客户端:

Foo updatedInstance = new Foo("newName"); 
updatedInstance.setId(createResponse.getBody().getId()); 
String resourceUrl =  
  fooResourceUrl + '/' + createResponse.getBody().getId(); 
HttpEntity<Foo> requestUpdate = new HttpEntity<>(updatedInstance, headers); 
template.exchange(resourceUrl, HttpMethod.PUT, requestUpdate, Void.class); 

5.2. 使用exchange实现简单put操作

接下来使用请求回调来发出PUT请求。首先定义回调方法————设置所有必要的头信息以及请求体:

RequestCallback requestCallback(final Foo updatedInstance) { 
    return clientHttpRequest -> { 
        ObjectMapper mapper = new ObjectMapper(); 
        mapper.writeValue(clientHttpRequest.getBody(), updatedInstance); 
        clientHttpRequest.getHeaders().add( 
          HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); 
        clientHttpRequest.getHeaders().add( 
          HttpHeaders.AUTHORIZATION, "Basic " + getBase64EncodedLogPass()); 
    }; 
} 

requestCallback用于在请求发出之前修改请求头信息,实现一些额外公共业务功能,如加密或解密。

然后使用POST请求创建资源:

ResponseEntity<Foo> response = restTemplate 
  .exchange(fooResourceUrl, HttpMethod.POST, request, Foo.class); 
assertThat(response.getStatusCode(), is(HttpStatus.CREATED)); 

接着更新资源,这里第三个参数指明了回调函数,clientHttpResponse参数用于对响应信息进行自定义解析。

Foo updatedInstance = new Foo("newName"); 
updatedInstance.setId(response.getBody().getId()); 
String resourceUrl = fooResourceUrl + '/' + response.getBody().getId(); 
restTemplate.execute( 
  resourceUrl,  
  HttpMethod.PUT,  
  requestCallback(updatedInstance),  
  clientHttpResponse -> null); 

6. 使用DELETE删除资源

使用delete() 方法删除资源:

String entityUrl = fooResourceUrl + "/" + existingResource.getId(); 
restTemplate.delete(entityUrl); 

7. 配置超时

可以简单通过 ClientHttpRequestFactory 配置RestTemplate超时设置:

RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory()); 
  
private ClientHttpRequestFactory getClientHttpRequestFactory() { 
    int timeout = 5000; 
    HttpComponentsClientHttpRequestFactory clientHttpRequestFactory 
      = new HttpComponentsClientHttpRequestFactory(); 
    clientHttpRequestFactory.setConnectTimeout(timeout); 
    return clientHttpRequestFactory; 
} 

也可以通过HttpClient进行更多的配置:

private ClientHttpRequestFactory getClientHttpRequestFactory() { 
    int timeout = 5000; 
    RequestConfig config = RequestConfig.custom() 
      .setConnectTimeout(timeout) 
      .setConnectionRequestTimeout(timeout) 
      .setSocketTimeout(timeout) 
      .build(); 
    CloseableHttpClient client = HttpClientBuilder 
      .create() 
      .setDefaultRequestConfig(config) 
      .build(); 
    return new HttpComponentsClientHttpRequestFactory(client); 
} 

8. 总结

本文介绍了通过RestTemplate实现调用HTTP的各种请求操作。实际开发中用得最多请求方式为GET和POST,其他方式为了安全考虑一般在应用服务器层配置禁用。


本文参考链接:https://blog.csdn.net/neweastsun/article/details/100749098
阅读延展