本文基于《Spring實(shí)戰(zhàn)(第4版)》所寫。
Rest含義:
- 表述性(Representational):REST資源實(shí)際上可以用各種形式來進(jìn)行表述,包括XML、JSON(JavaScript Object Notation)甚至HTML—最適合資源使用者的任意形式;
- 狀態(tài)(State):當(dāng)使用REST的時(shí)候,我們更關(guān)注資源的狀態(tài)而不是對(duì)資源采取的行為;
- 轉(zhuǎn)移(Transfer):REST涉及到轉(zhuǎn)移資源數(shù)據(jù),它以某種表述性形式從一個(gè)應(yīng)用轉(zhuǎn)移到另一個(gè)應(yīng)用。
REST的動(dòng)作(HTTP的方法)以及匹配的CRUD動(dòng)作:
- Create: POST
- Read: GET
- Update: PUT或PATCH
- DELETE: DELETE
Spring支持以下方式來創(chuàng)建REST資源:
- 控制器可以處理所有的HTTP方法,包含四個(gè)主要的REST方法:GET、PUT、DELETE以及POST。Spring 3.2及以上版本還支持PATCH方法;
- 借助@PathVariable注解,控制器能夠處理參數(shù)化的URL(將變量輸入作為URL的一部分);
- 借助Spring的視圖和視圖解析器,資源能夠以多種方式進(jìn)行表述,包括將模型數(shù)據(jù)渲染為XML、JSON、Atom以及RSS的View實(shí)現(xiàn);
- 可以使用ContentNegotiatingViewResolver來選擇最適合客戶端的表述;
- 借助@ResponseBody注解和和各種HttpMethodConverter實(shí)現(xiàn),能夠替換基于視圖的渲染方式;
- 類似地,@RequestBody注解以及HttpMethodConverter實(shí)現(xiàn)可以將傳入的HTTP數(shù)據(jù)轉(zhuǎn)化為傳入控制器處理方法的Java對(duì)象;
- 借助RestTemplate,Spring應(yīng)用能夠方便地使用Rest資源。
Spring提供了兩種方法將資源的Java表述轉(zhuǎn)換為發(fā)送給客戶端的表述形式:
- 內(nèi)容協(xié)商(Content negotiation):選擇一個(gè)視圖,它能夠?qū)⒛P弯秩緸槌尸F(xiàn)給客戶端表述形式。不過由于它只能決定資源該如何渲染到客戶端,并沒有涉及到客戶端要發(fā)送什么樣的表述給控制器使用,比如客戶端發(fā)送JSON或XML,它就無法提供幫助了。而且還有其他限制,不建議使用。
- 消息轉(zhuǎn)換器(Message conversion):通過一個(gè)消息轉(zhuǎn)換器將控制器所返回的對(duì)象轉(zhuǎn)換為呈現(xiàn)給客戶端的表述形式。
使用HTTP信息轉(zhuǎn)換器
當(dāng)使用消息轉(zhuǎn)換功能時(shí),DispatcherServlet不再將模型數(shù)據(jù)傳送到視圖中。實(shí)際上,根本就沒有模型,也沒有視圖,只有控制器產(chǎn)生的數(shù)據(jù),以及消息轉(zhuǎn)換器轉(zhuǎn)換數(shù)據(jù)之后所產(chǎn)生的資源表述。
Spring自帶了各種各樣的轉(zhuǎn)換器,比如客戶端通過請(qǐng)求的Accept頭信息表明它能接受“application/json”,并且Jackson JSON在類路徑下,那么處理方法返回的對(duì)象將交給MappingJacksonHttpMessageConverter,并由它轉(zhuǎn)換為返回客戶端的JSON表述形式。大部分轉(zhuǎn)換器都是自動(dòng)注冊(cè)的,不需要Spring配置。但是為了支持它們,需要添加一些庫到應(yīng)用程序的類路徑下。
如果使用了消息轉(zhuǎn)換功能的話,我們需要告訴Spring跳過正常的模型/視圖流程,并使用消息轉(zhuǎn)換器。最簡(jiǎn)單的方式是為控制器方法添加@ResponseBody注解。例如,如下程序:
@RequestMapping(method=RequestMethod.GET, produces="application/json")
public @ResponseBody List<Spittle> spittles (
@RequestParam(value="max",defaultValue=MAX_LONG_AS_SPRING)) long max,
@RequestParam(value="count",defaultValue="20") int count) {
return spittleRepository.findSpittles(max, count);
}
@ResponseBody注解會(huì)告知Spring,我們要將返回的對(duì)象作為資源發(fā)送給客戶端,并將其轉(zhuǎn)換為客戶端可接受的表述形式。更具體地講,DispatcherServlet將會(huì)考慮到請(qǐng)求中Accept頭部信息,并查找能夠?yàn)榭蛻舳颂峁┧璞硎鲂问降南⑥D(zhuǎn)換器(根據(jù)類路徑下實(shí)現(xiàn)庫)。
需要注意的是,默認(rèn)情況下,Jackson JSON庫在將返回的對(duì)象轉(zhuǎn)換為JSON資源表述時(shí),會(huì)使用反射。如果重構(gòu)了Java類型,比如添加、移除或重命名屬性,那么產(chǎn)生的JSON也將會(huì)發(fā)生變化。但是,我們可以在Java類型上使用Jackson的映射注解,改變產(chǎn)生JSON的行為。
談及Accept頭部信息,在@RequestMapping注解中,我們使用了produces屬性表明這個(gè)方法只處理預(yù)期輸出為JSON的請(qǐng)求,其他任何類型的請(qǐng)求,都不會(huì)被這個(gè)方法處理。這樣的請(qǐng)求會(huì)被其他的方法來進(jìn)行處理,或者返回客戶端HTTP 406響應(yīng)。
與@ResponseBody類似,@RequestBody也能告訴Spring查找一個(gè)消息轉(zhuǎn)換器,將來自客戶端的資源表述為對(duì)象。例如:
@RequestMapping(method=RequestMethod.POST, consumes="application/json")
public @ResponseBody Spittle saveSpittle(@RequestBody Spittle spittle) {
return spittleRepository.save(spittle);
}
通過使用注解,@RequestMapping表明它只能處理“/spittles”(在類級(jí)別的@RequestMapping中進(jìn)行了聲明)的POST請(qǐng)求。POST請(qǐng)求體中預(yù)期要包含一個(gè)Spittle的資源表述。因?yàn)镾pittle參數(shù)上使用了@RequestBody,所以Spring將會(huì)查看請(qǐng)求中的Content-Type頭部信息,并查找能夠?qū)⒄?qǐng)求轉(zhuǎn)換為Spittle的消息轉(zhuǎn)換器。
例如,如果客戶端發(fā)送的Spittle數(shù)據(jù)是JSON表述形式,那么Content-Type頭部信息可能就會(huì)是“application/json”。在這種情況下,DispatcherServlet會(huì)查找能夠?qū)SON轉(zhuǎn)換為Java對(duì)象的消息轉(zhuǎn)換器。
注意,@RequestMapping有一個(gè)consumes屬性,我們將其設(shè)置為“application/json”。consumes屬性的工作方式類似于produces,不過它會(huì)關(guān)注請(qǐng)求的Content-Type頭部信息。它會(huì)告訴Spring這個(gè)方法只會(huì)處理對(duì)“/spittles”的POST請(qǐng)求,并且要求請(qǐng)求的Content-Type頭部信息為“application/json”。如果無法滿足這些條件的話,會(huì)有其他方法來處理請(qǐng)求。
Spring 4.0引入了@RestController注解。如果在控制器類上使用@RestController來代替@Controller的話,Spring將會(huì)為該控制器的所有處理方法應(yīng)用消息轉(zhuǎn)換功能。我們不必為每個(gè)方法都添加@ResponseBody了。添加@RestController注解,此類中所有處理器方法都不需要使用@ResponseBody注解了,因?yàn)榭刂破魇褂昧薂RestController,所有它的方法所返回的對(duì)象將會(huì)通過消息轉(zhuǎn)換機(jī)制,產(chǎn)生客戶端所需的資源表述。
發(fā)送錯(cuò)誤信息到客戶端
如果一個(gè)處理器方法本應(yīng)返回一個(gè)對(duì)象,但由于查找不到相應(yīng)的對(duì)象而返回null。我們考慮一下在這種場(chǎng)景下應(yīng)該發(fā)生什么。至少,狀態(tài)碼不應(yīng)是200,而應(yīng)該是404,告訴客戶端它們所要求的內(nèi)容沒有找到。如果響應(yīng)體中能夠包含錯(cuò)誤信息而不是空的話就更好了。
Spring提供了多種方式來處理這樣的場(chǎng)景:
- 使用@ResponseStatus注解可以指定狀態(tài)碼;
- 控制器方法可以返回ResponseEntity對(duì)象,該對(duì)象能夠包含更多響應(yīng)相關(guān)的元數(shù)據(jù);
- 異常處理器能夠應(yīng)對(duì)錯(cuò)誤場(chǎng)景,這樣處理器方法就能關(guān)注于正常的狀況。
使用ResponseEntity
作為@ResponseBody的替代方案,控制器方法可以返回一個(gè)ResponseEntity對(duì)象。ResponseEntity中可以包含響應(yīng)相關(guān)的元數(shù)據(jù)(如頭部信息和狀態(tài)碼)以及要轉(zhuǎn)換成資源表述的對(duì)象。
@RequestMapping(value="/{id}", method=RequestMethod.GET)
public ResponseEntity<Spittle> spittleById(@PathVariable long id) {
Spittle spittle = spittleRepository.findOne(id);
HttpStatus status = spittle != null ? HttpStatus.OK : HttpStatus.NOT_FOUND;
return new RepositoryEntity<Spittle>(spittle, status);
}
注意,如果返回ResponseEntity的話,那就沒有必要在方法上使用@ResponseBody注解了。
如果我們希望在響應(yīng)體中包含一些錯(cuò)誤信息。我們需要定義一個(gè)包含錯(cuò)誤信息的Error對(duì)象:
public class Error {
private int code;
private String message;
public Error(int code ,String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
public String getMessage() {
return message;
}
}
然后,我們可以修改spittleById(),讓它返回Error:
@RequestMapping(value="/{id}", method=RequestMethod.GET)
public ResponseEntity<?> spittleById(@PathVariable long id ) {
Spittle spittle = spittleRepository.findOne(id);
if (spittle == null) {
Error error = new Error(4, "Spittle [" + id + "] not found");
return new ResponseEntity<Error> (error, HttpStatus.NOT_FOUND);
}
return new ResponseEntity<Spittle>(spittle, HttpStatus.OK);
}
處理錯(cuò)誤
我們重構(gòu)一下代碼來使用錯(cuò)誤處理器。首先,定義能夠?qū)ο骃pittleNotFoundException的錯(cuò)誤處理器:
@ExceptionHandler(SpittleNotFoundException.class)
public ResponseEntity<Error> spittleNotFound(SpittleNotFoundException e) {
long spittleId = e.getSpittleId();
Error error = new Error(4, "Spittle [" + spittleId + "] not found");
return new ResponseEntity<Error> (error, HttpStatus.NOT_FOUND);
}
@ExceptionHandler注解能夠用到控制器方法中,用來處理特定的異常。至于SpittleNotFoundException,它是一個(gè)很簡(jiǎn)單異常類:
public class SpittleNotFoundException extends RuntimeException {
private long spittleId;
public SpittleNotFoundException(long spittleId) {
this.spittleId = spittleId;
}
public long getSpittleId() {
return spittleId;
}
}
現(xiàn)在,我們可以移除掉spittleById() 方法中大多數(shù)的錯(cuò)誤代碼:
@RequestMapping(value="/{id}" , method=RequestMethod.GET)
public ResponseEntity<Spittle> spittleById(@PathVariable long id) {
Spittle spittle = spittleRepository.findOne(id);
if (spittle == null) { throw new SpittleNotFoundException(id); }
return new ResponseEntity<Spittle>(spittle, HttpStatus.OK);
}
更簡(jiǎn)潔的版本是(控制器類上使用@RestController)
@RequestMapping(value="/{id}" , method=RequestMethod.GET)
public Spittle spittleById(@PathVariable long id) {
Spittle spittle = spittleRepository.findOne(id);
if (spittle == null) { throw new SpittleNotFoundException(id); }
return spittle;
}
鑒于錯(cuò)誤處理器的方法會(huì)始終返回Error,并且HTTP狀態(tài)碼為404,那么現(xiàn)在我們可以對(duì)spittleNotFound() 方法進(jìn)行類似的清理(控制器類上使用@RestController):
@ExceptionHandler(SpittleNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public Error spittleNotFound(SpittleNotFoundException e) {
long spittleId = e.getSpittleId();
return Error error = new Error(4, "Spittle [" + spittleId + "] not found");
}
在響應(yīng)中設(shè)置頭部信息
如果我們需要在POST請(qǐng)求后,返回201且把資源的URL返回給客戶端,可以用@ResponseEntity實(shí)現(xiàn)
@RequestMapping(method=RequestMethod.POST, consumes="application/json")
public ResponseEntity<Spittle> saveSpittle(@RequestBody Spittle spittle) {
Spittle spittle = spittleRepository.save(spittle);
HttpHeaders headers = new HttpHeaders();
URI locationUri = URI.create("http://localhost:8080/spittr/spittles/" + spittle.getId());
headers.setLocation(locationUri);
ResponseEntity<Spittle> responseEntity =
new ResponseEntity<Spittle>(spittle, headers, HttpStatus.CREATED);
return responseEntity;
}
其實(shí)我們沒有必要手動(dòng)構(gòu)建URL,Spring 提供了UriComponentsBuilder。它是一個(gè)構(gòu)建類,通過逐步指定URL中的各種組成部分(如host、端口、路徑以及查詢),我們能夠使用它來構(gòu)建UriComponents實(shí)例。
為了使用UriComponentsBuilder,我們需要做的就是在處理器方法中將其作為一個(gè)參數(shù),如下面的程序清單所示。
@RequestMapping(method=RequestMethod.POST, consumes="application/json")
public ResponseEntity<Spittle> saveSpittle(@RequestBody Spittle spittle,
UriComponentsBuilder ucb) {
Spittle spittle = spittleRepository.save(spittle);
HttpHeaders headers = new HttpHeaders();
URI locationUri = ucb.path("/spittles/").path(String.valueOf(spittle.getId()))
.build().toUri();
headers.setLocation(locationUri);
ResponseEntity<Spittle> responseEntity =
new ResponseEntity<Spittle>(spittle, headers, HttpStatus.CREATED);
return responseEntity;
}
在處理器方法所得到的UriComponentsBuilder中,會(huì)預(yù)先配置已知的信息如host、斷端口以及Servlet內(nèi)容。
注意,路徑的構(gòu)建分為兩步。第一步調(diào)用path()方法,將其設(shè)置“/spittles/”,也就是這個(gè)控制器所能處理的基礎(chǔ)路徑。然后,在第二次調(diào)用path()的時(shí)候,使用了已使用Spittle的ID。在路徑設(shè)置完成之后,調(diào)用build()方法來構(gòu)建UriComponents對(duì)象,根據(jù)這個(gè)對(duì)象調(diào)用toUri()就能得到新創(chuàng)建Spittle的URI。
了解RestTemplate的操作
RestTemplate可以減少我們使用HttpClient創(chuàng)建客戶端所帶來的樣板式代碼。它定義了36個(gè)(只有11個(gè)獨(dú)立方法,其他都是重載這些方法)與REST資源交互的方法,其中的大多數(shù)都對(duì)應(yīng)于HTTP的方法。下表展示了這11個(gè)獨(dú)立方法
| 方法 | 描述 |
|---|---|
| delete() | 在特定的URL上對(duì)資源執(zhí)行HTTP DELETE操作 |
| exchange() | 在URL上執(zhí)行特定的HTTP方法,返回包含對(duì)象的ResponseEntity,這個(gè)對(duì)象是從響應(yīng)體中映射得到的 |
| execute() | 在URL上執(zhí)行特定的HTTP方法,返回一個(gè)從響應(yīng)體映射得到的對(duì)象 |
| getForEntity() | 發(fā)送一個(gè)HTTP GET請(qǐng)求,返回的ResponseEntity包含了響應(yīng)體所映射成的對(duì)象 |
| getForObject() | 發(fā)送一個(gè)HTTP GET請(qǐng)求,返回的請(qǐng)求體將映射為一個(gè)對(duì)象 |
| headForHeaders() | 發(fā)送HTTP HEAD請(qǐng)求,返回包含特定資源URL的HTTP頭 |
| optionsForAllow() | 發(fā)送HTTP OPTIONS請(qǐng)求,返回對(duì)特定的URL的Allow頭信息 |
| postForEntity() | POST數(shù)據(jù)到一個(gè)URL,返回包含一個(gè)對(duì)象的ResponseEntity,這個(gè)對(duì)象是從響應(yīng)體中映射得到的 |
| postForLocation() | POST數(shù)據(jù)到一個(gè)URL,返回新創(chuàng)建資源的URL |
| postForObject() | POST數(shù)據(jù)到一個(gè)URL,返回根據(jù)響應(yīng)體匹配形成的對(duì)象 |
| put() | PUT資源到特定的URL |
GET資源
getForObject()都有三種形式的重載
<T> T getForObject(URI url, Class<T> responseType)
throws RestClientException;
<T> T getForObject(String url, Class<T> responseType, Object... uriVariables)
throws RestClientException;
<T> T getForObject(String url, Class<T> responseType,
Map<String,?> uriVariables) throws RestClientException;
檢索資源
public Profile fetchFacebookProfile(String id) {
RestTemplate rest = new RestTemplate();
return rest.getForObject("http://graph.facebook.com/{spritter}",Profile.class, id);
}
另一種方案
public Profile fetchFacebookProfile(String id) {
Map<String, String> urlVariables = new HashMap<>();
urlVariables.put("id", id);
RestTemplate rest = new RestTemplate();
return rest.getForObject("http://graph.facebook.com/{spritter}",
Profile.class, urlVariables);
}
getForEntity()都有三種形式的重載
<T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType)
throws RestClientException;
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType,
Object... uriVariables) throws RestClientException;
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType,
Map<String,?> uriVariables) throws RestClientException;
抽取響應(yīng)的元數(shù)據(jù)
public Spittle fetchSpittle(long id) {
RestTemplate rest = new RestTemplate();
ResponseEntity<Spittle> response = rest.getForEntity(
"http://localhost:8080/spittr-api/spittles/{id}",
Spittle.class, id);
if(response.getStatusCode() == HttpStatus.NOT_MODIFIED) {
throw new NotModifiedException();
}
return response.getBody();
}
PUT資源
put() 有三種形式:
void put(URI url, Object request) throws RestClientException;
void put(String url, Object request, Object... uriVariables)
throws RestClientException;
void put(String url, Object request, Map<String, ?> uriVariables)
throws RestClientException;
例如
public void updateSpittle( Spittle spittle) throws SpitterException {
RestTemplate rest = new RestTemplate();
String url = "http://localhost:8080/spittr-api/spittles/" + spittle.getId();
rest.put(URI.create(url), spittle);
}
public void updateSpittle( Spittle spittle) throws SpitterException {
RestTemplate rest = new RestTemplate();
String url = "http://localhost:8080/spittr-api/spittles/{id}";
rest.put(url, spittle, spittle.getId());
}
DLELTE資源
delete()方法有三個(gè)版本
void delete(String url ,Object... uriVariables) throws RestClientException;
void delete(String url ,Map<String, ?> uriVariables) throws RestClientException;
void delete(URI url) throws RestClientException;
POST資源數(shù)據(jù)
postForObject() 方法的三個(gè)變種簽名如下:
<T> T postForObject(URI url, Object request, Class<T> responseType)
throws RestClientException;
<T> T postForObject(String url, Object request, Class<T> responseType,
Object... uriVariables) throws RestClientException;
<T> T postForObject(String url, Object request, Class<T> responseType,
Map<String, ?> uriVariables) throws RestClientException;
在所有情況下,第一個(gè)參數(shù)都是資源要POST的URL,第二個(gè)參數(shù)是要發(fā)送的對(duì)象,而第三個(gè)參數(shù)是預(yù)期返回的Java類型。在將URL作為String類型的兩個(gè)版本中,第四個(gè)參數(shù)指定了URL變量(要么是可變參數(shù)列表,要么是一個(gè)Map)。
例如
public Spitter postSpitterForObject(Spitter spitter) {
RestTemplate rest = new RestTemplate();
return rest.postForObject("http://localhost:8080/spittr-api/spitters",
spitter, Spitter.class);
}
postForEntity() 方法的三個(gè)變種簽名如下:
<T> ResponseEntity<T> postForEntity(URI url, Object request,
Class<T> responseType) throws RestClientException;
<T> ResponseEntity<T> postForEntity(String url, Object request,
Class<T> responseType, Object... uriVariables)
throws RestClientException;
<T> ResponseEntity<T> postForEntity(String url, Object request,
Class<T> responseType, Map<String, ?> uriVariables)
throws RestClientException;
例如:
RestTemplate rest = new RestTemplate();
ResponseEntity<Spitter> response = rest.postForEntity(
"http://localhost:8080/spittr-api/spitters",
spitter, Spitter.class);
Spitter spitter = response.getBody();
URI url = response.getHeaders().getLocation();
如果只是需要的是Location頭信息的值,那么使用RestTemplate的postForLocation()方法會(huì)更簡(jiǎn)單。以下是postForLocation()的三個(gè)方法簽名:
URI postForLocation(String url, Object request, Object... uriVariables)
throws RestClientException;
URI postForLocation(String url, Object request, Map<String,?> uriVariables)
throws RestClientException;
URI postForLocation(URI url, Object request) throws RestClientException;
例如:
public String postSpitter(Spitter spitter) {
RestTemplate rest = new RestTemplate();
return rest.postForLocation(
"http://localhost:8080/spittr-api/spitters",
spitter).toString();
}
交換資源
如果想在發(fā)送給服務(wù)端的請(qǐng)求中設(shè)置頭信息的話,那就是RestTemplate的exchange()的用武之地了。
exchange()也有三個(gè)簽名格式
<T> ResponseEntity<T> exchange(URI url, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType)
throws RestClientException;
<T> ResponseEntity<T> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType,
Object... uriVariables) throws RestClientException;
<T> ResponseEntity<T> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType,
Map<String,?> uriVariables) throws RestClientException;
exchange() 方法使用HttpMethod參數(shù)來表明要使用的HTTP動(dòng)作。根據(jù)這個(gè)參數(shù)的值,exchange()能夠執(zhí)行與其他RestTemplate方法一樣的工作。
例如,從服務(wù)器端獲取Spitter資源的一種方式是使用RestTemplate的getForEntity()方法,如下所示:
ResponseEntity<Spitter> response = rest.getForEntity(
"http://localhost:8080/spittr-api/spitters/{spitter}",
Spitter.class, spitterId);
Spitter spitter = response.getBody();
在下面的代碼片段中,可以看到exchange() 也可以完成這項(xiàng)任務(wù):
ResponseEntity<Spitter> response = rest.exchange(
"http://localhost:8080/spittr-api/spitters/{spitter}",
HttpMethod.GET, null ,Spitter.class, spitterId);
Spitter spitter = response.getBody();
如果不指明頭信息,exchange() 對(duì)Spitter的GET請(qǐng)求會(huì)帶有如下的頭信息:
GET /Spitter/spitters/habuma HTTP/1.1
Accept: application/xml, test/xml, application/*+xml, application/json
Content-Length: 0
User-Agent: Java/1.6.0_20
Host: location:8080
Connection: keep-alive
如果我們需要將“application/json”設(shè)置為Accept頭信息的唯一值。
設(shè)置請(qǐng)求頭信息是很簡(jiǎn)單的,只需要構(gòu)造發(fā)送給exchange()方法的 HttpEntity對(duì)象即可,HttpEntity中包含承載頭信息的MultiValueMap:
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add("Accept", "application/json");
HttpEntity<Object> requestEntity = new HttpEntity<Object>(headers);
如果這是一個(gè)PUT或POST請(qǐng)求,我們需要為HttpEntity設(shè)置在請(qǐng)求體中發(fā)送的對(duì)象—對(duì)于GET請(qǐng)求來說,這是沒有必要的。
現(xiàn)在我們可以傳入HttpEntity來調(diào)用exchange();
ResponseEntity<Spitter> response = rest.exchange(
"http://localhost:8080/spittr-api/spitters/{spitter}",
HttpMethod.GET, headers ,Spitter.class, spitterId);
Spitter spitter = response.getBody();