情景描述:客户端新增资源对象,服务端保存资源并返回信息。
@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注解**
@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就能提供帮助了。
如下的程序清单展现了一个新版本的saveCity(),它会返回ResponseEntity用来告诉客户端新创建的资源。
/**
* @author gucailiang
* @date 2018/10/10
*/
@Controller
@RequestMapping(value = "city")
public class CityController {
@Autowired
private CityService cityService;
/**
* 当创建新资源的时候,将资源的URL放在响应的Location头部信息中,并返回给客户端是一种很好的方式。
* 因此,我们需要有一种方式来填充响应头部信息,此时我们的老朋友ResponseEntity就能提供帮助了。
*
* 注意: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
<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的特殊实现,它有一些便利的Setter方法(如setLocation()),用来设置常见的HTTP头部信息。
在得到新创建City资源的URL之后,接下来使用这个头部信息来创建ResponseEntity
使用硬编码值的方式来构建Location头部信息。URL中“localhost”以及“8080”这两个部分尤其需要注意,因为如果我们将应用部署到其他地方,而不是在本地运行的话,它们就不适用了.
使用UriComponentsBuilder,我们需要做的就是在处理器方法中将其作为一个参数.
@Controller
@RequestMapping(value = "city")
public class CityController {
/**
* 原本简单的saveSpittle()方法瞬间变得臃肿了。但是,更值得关注的是,它使用硬编码值的方式来构建Location头部信息。
* URL中“localhost”以及“8080”这两个部分尤其需要注意,因为如果我们将应用部署到其他地方,而不是在本地运行的话,它们就不适用了
*
* 其实没有必要手动构建URL,Spring提供了UriComponentsBuilder,可以给我们一些帮助。它是一个构建类,通过逐步指定URL中的各种组成部分(如host、端口、路径以及查询),
* 我们能够使用它来构建UriComponents实例。借助UriComponentsBuilder所构建的UriComponents对象,我们就能获得适合设置给Location头部信息的URI。
*
* 为了使用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没有人关心和使用的话,那也没有什么价值