【Spring实战(第四版)笔记】——REST在响应中设置头部信息

——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就能提供帮助了。
     * 

* 注意: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

使用UriComponentsBuilder解决响应Location头部信息的硬编码问题

使用硬编码值的方式来构建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没有人关心和使用的话,那也没有什么价值

你可能感兴趣的:(spring)