【Spring實戰(第四版)筆記】——REST在響應中設定頭部資訊
<Spring實戰(第四版)筆記>——REST在響應中設定頭部資訊
情景描述:客戶端新增資源物件,服務端儲存資源並返回資訊。
@RestController
@RequestMapping(value = "city")
public class CityHeaderController {
@Autowired
private CityService cityService;
/**
* @param city
* @return
*/
@RequestMapping(value = "/api/add", method = RequestMethod.POST)
public @ResponseBody Long saveCity(@RequestBody City city) {
return cityService.saveCity(city);
}
}
在saveCity()處理完請求之後,伺服器在響應體中包含了City的表述以及HTTP狀態碼200(OK),將其返回給客戶端。這裡沒有什麼大問題,但是還不是完全準確。
當然,假設處理請求的過程中成功建立了資源,狀態可以視為OK。但是,我們不僅僅需要說“OK”。我們建立了新的內容,HTTP狀態碼也將這種情況告訴給了客戶端。
不過,HTTP 201不僅能夠表明請求成功完成,而且還能描述建立了新資源。如果我們希望完整準確地與客戶端交流,那麼響應是不是應該為201(Created),而不僅僅是200(OK)呢?
Spring提供了方式來處理這樣的場景:
** 我們需要做的就是為方法新增@ResponseStatus註解**
新增@ResponseStatus註解,返回指定的狀態碼
@RestController
@RequestMapping(value = "city")
public class CityHeaderController {
@Autowired
private CityService cityService;
/**
* HTTP 201不僅能夠表明請求成功完成,而且還能描述建立了新資源。
* 如果我們希望完整準確地與客戶端交流,那麼響應是不是應該為201(Created),而不僅僅是200(OK)
*
* @param city
* @return
*/
@RequestMapping(value = "/api1/add", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public @ResponseBody
Long saveCity1(@RequestBody City city) {
return cityService.saveCity(city);
}
}
當建立新資源的時候,將資源的URL放在響應的Location頭部資訊中,並返回給客戶端是一種很好的方式。
因此,我們需要有一種方式來填充響應頭部資訊,此時我們的老朋友ResponseEntity就能提供幫助了。
使用ResponseEntity,將資源的URL放在響應的Location頭部資訊
如下的程式清單展現了一個新版本的saveCity(),它會返回ResponseEntity用來告訴客戶端新建立的資源。
/**
* @author gucailiang
* @date 2018/10/10
*/
@Controller
@RequestMapping(value = "city")
public class CityController {
@Autowired
private CityService cityService;
/**
* 當建立新資源的時候,將資源的URL放在響應的Location頭部資訊中,並返回給客戶端是一種很好的方式。
* 因此,我們需要有一種方式來填充響應頭部資訊,此時我們的老朋友ResponseEntity就能提供幫助了。
* <p>
* 注意:mapper介面返回值依然是成功插入的記錄數,但不同的是主鍵值已經賦值到領域模型實體的id中了。
*/
@RequestMapping(value = "/api2/add", method = RequestMethod.POST, consumes = "application/json")
@ResponseStatus(HttpStatus.CREATED)
public @ResponseBody
ResponseEntity<City> saveCity2(@RequestBody City city) {
HttpHeaders httpHeaders = new HttpHeaders();
Long id = cityService.saveCity(city);
URI locationUrl = URI.create("localhost:8081/city/api5/" + city.getId());
httpHeaders.setLocation(locationUrl);
return new ResponseEntity<City>(city, httpHeaders, HttpStatus.CREATED);
}
}
mapper介面的sql
<!--useGeneratedKeys="true" keyProperty="id" 自動生產主鍵,
並把主鍵繫結在keyProperty對應的屬性中,mapper介面返回值依然是成功插入的記錄數,
但不同的是主鍵值已經賦值到領域模型實體的id中了-->
<insert id="saveCity" parameterMap="City" useGeneratedKeys="true" keyProperty="id">
insert into
city(id,province_id,city_name,description)
values
(#{id},#{provinceId},#{cityName},#{description})
</insert>
這個新的版本中,我們建立了一個HttpHeaders例項,用來存放希望在響應中包含的頭部資訊值。
HttpHeaders是MultiValueMap<String, String>的特殊實現,它有一些便利的Setter方法(如setLocation()),用來設定常見的HTTP頭部資訊。
在得到新建立City資源的URL之後,接下來使用這個頭部資訊來建立ResponseEntity
使用UriComponentsBuilder解決響應Location頭部資訊的硬編碼問題
使用硬編碼值的方式來構建Location頭部資訊。URL中“localhost”以及“8080”這兩個部分尤其需要注意,因為如果我們將應用部署到其他地方,而不是在本地執行的話,它們就不適用了.
使用UriComponentsBuilder,我們需要做的就是在處理器方法中將其作為一個引數.
@Controller
@RequestMapping(value = "city")
public class CityController {
/**
* 原本簡單的saveSpittle()方法瞬間變得臃腫了。但是,更值得關注的是,它使用硬編碼值的方式來構建Location頭部資訊。
* URL中“localhost”以及“8080”這兩個部分尤其需要注意,因為如果我們將應用部署到其他地方,而不是在本地執行的話,它們就不適用了
* <p>
* 其實沒有必要手動構建URL,Spring提供了UriComponentsBuilder,可以給我們一些幫助。它是一個構建類,通過逐步指定URL中的各種組成部分(如host、埠、路徑以及查詢),
* 我們能夠使用它來構建UriComponents例項。藉助UriComponentsBuilder所構建的UriComponents物件,我們就能獲得適合設定給Location頭部資訊的URI。
* <p>
* 為了使用UriComponentsBuilder,我們需要做的就是在處理器方法中將其作為一個引數
*/
@RequestMapping(value = "/api3/add", method = RequestMethod.POST, consumes = "application/json")
@ResponseStatus(HttpStatus.CREATED)
public @ResponseBody
ResponseEntity<City> saveCity3(@RequestBody City city, UriComponentsBuilder uriComponentsBuilder) {
HttpHeaders httpHeaders = new HttpHeaders();
Long id = cityService.saveCity(city);
URI locationUrl = uriComponentsBuilder.path("city/api5/").path(String.valueOf(city.getId())).build().toUri();
httpHeaders.setLocation(locationUrl);
return new ResponseEntity<City>(city, httpHeaders, HttpStatus.CREATED);
}
}
請求返回的headers中location如下:
content-type →application/json;charset=UTF-8
location →http://localhost:8081/city/api5/city/api5/city/api5/22
在REST API中暴露資源只代表了會話的一端。如果釋出的API沒有人關心和使用的話,那也沒有什麼價值