最近在做Google AMP mail的时候遇到了一个问题,在调用/unsub(退订)接口的时候需要向google client返回特定的ResponseHeader。但是项目使用的是Springboot 2.x,通常使用@RestController修饰API层,无法做到动态的返回特殊的Header。那么如何对一些特殊的API做一些特别的返回值封装呢?Spring framework中的ResponseEntity类很好的解决了此问题。
众所周知,我们的HTTP/HTTPS的响应由状态码、头部信息以及响应体内容三大块组成,响应体内容可以通过返回值进行一系列封装回传出结果,而状态码与头部信息需要特别设置。
先上源码,看看ResponseEntity到底如何使用的。
public class ResponseEntity<T> extends HttpEntity<T> {
public ResponseEntity(HttpStatus status) {
this((Object)null, (MultiValueMap)null, (HttpStatus)status);
}
public ResponseEntity(@Nullable T body, HttpStatus status) {
this(body, (MultiValueMap)null, (HttpStatus)status);
}
public ResponseEntity(MultiValueMap<String, String> headers, HttpStatus status) {
this((Object)null, headers, (HttpStatus)status);
}
public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, HttpStatus status) {
super(body, headers);
Assert.notNull(status, "HttpStatus must not be null");
this.status = status;
}
private ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, Object status) {
super(body, headers);
Assert.notNull(status, "HttpStatus must not be null");
this.status = status;
}
}
可以看到这个类提供了五个构造方法,返回的状态码是必传的外,头部信息以及响应体内容都是可选择的。当我们需要使用的时候,直接new一个ResponseEntity对象作为API返回值即可,这就是它的第一种使用方式。
需要注意的是,在有headers作为参数的构造方法中,需要传入一个类型为MultiValueMap
HttpHeaders headers = new HttpHeaders();
headers.add("Access-Control-Expose-Headers", "AMP-Access-Control-Allow-Source-Origin");
return new ResponseEntity<>(resultBody, headers, HttpStatus.OK);
继续看源码,发现了好多返回值为ResponseEntity.BodyBuilder的方法有木有
public static ResponseEntity.BodyBuilder status(HttpStatus status) {
Assert.notNull(status, "HttpStatus must not be null");
return new ResponseEntity.DefaultBuilder(status);
}
public static ResponseEntity.BodyBuilder status(int status) {
return new ResponseEntity.DefaultBuilder(status);
}
public static ResponseEntity.BodyBuilder ok() {
return status(HttpStatus.OK);
}
public static ResponseEntity.BodyBuilder created(URI location) {
ResponseEntity.BodyBuilder builder = status(HttpStatus.CREATED);
return (ResponseEntity.BodyBuilder)builder.location(location);
}
public static ResponseEntity.BodyBuilder accepted() {
return status(HttpStatus.ACCEPTED);
}
public static ResponseEntity.HeadersBuilder<?> noContent() {
return status(HttpStatus.NO_CONTENT);
}
public static ResponseEntity.BodyBuilder badRequest() {
return status(HttpStatus.BAD_REQUEST);
}
public static ResponseEntity.HeadersBuilder<?> notFound() {
return status(HttpStatus.NOT_FOUND);
}
public static ResponseEntity.BodyBuilder unprocessableEntity() {
return status(HttpStatus.UNPROCESSABLE_ENTITY);
}
.........
事实证明,越是伟大的软件工程师是越“懒惰”的,为了节约时间增加效率,我们可以用Builder的方式去生成ResponseEntity对象并返回:
Map<String, String> resultBody = new HashMap<>();
resultBody.put("name":"Lucas");
resultBody.put("school":"harvard university");
return ResponseEntity.status(HttpStatus.OK)
.body(resultBody);
@RestController = @Controller + @ResponseBody
@ResponseBody是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据。
注意:在使用此注解之后不会再走视图处理器(ModelAndView),而是直接将数据写入到输入流中,他的效果等同于通过response对象输出指定格式的数据。
这个注解主要用在自定义的Exception 上,或者直接用在controller层的API方法上也可,当发生异常/方法执行结束时,会返回相应的Http状态码和msg。
@ResponseStatus(value=HttpStatus.FORBIDDEN,reason="不允许访问")
注:
ResponseEntity的优先级高于@ResponseBody。只有在返回值不为ResponseEntity的情况下才去检查有没有@ResponseBody注解;如果响应类型是ResponseEntity则会忽略@ResponseBody注解。
ResponseEntity能够非常方便的修改返回值的状态码,但最优秀的用法仍然是能够为不同API设置不同的返回响应头。