RestTemplate简明教程

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,其他方式为了安全考虑一般在应用服务器层配置禁用。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值