@ResponseBody 提供了一种很有用的方式,能够将控制器返回的 Java 对象转换为发送到客户端的资源表述。
一个好的 REST API 不仅仅能够在客户端和服务器之间传递资源,他还能够给客户端提供额外的数据,帮助客户端理解资源或者在请求中发生了什么情况。
例如,我们为 UserController 中添加一个新的处理器方法,它会提供单个 Spittle 对象。
@RestController
@RequestMapping("/user")
public class UserController {
Map users = Collections.synchronizedMap(new HashMap());
@RequestMapping(value = "/{id}",method = RequestMethod.GET, produces = "application/json")
public User getUserById (@PathVariable("id") String id){
//初始化用户信息
initUserInfo();
return users.get(id);
}
private void initUserInfo() {
User user1 = new User("123","zhaoben");
users.put(user1.getId(),user1);
}
}
以上代码中,利用 Map 模拟数据库查找操作,通过id(123)进行查询,然后他根据调用 getUserById() 方法,查找对应 User 对象。 处理器方法返回查询结果。消息转换器会负责产生客户端所需要的资源表述。
*如果,根据给定 id,无法找到某个 User 对象,则 getUserById 返回 null
的同时,HTTP 状态码为 200,所有的过程看起来都很合理。但是,所有的事情都是不对的,客户端要求 User 对象,但是它什么都没有得到。客户端既没有收到 User 对象,也没有收到任何消息表示出现了错误。*
Spring提供了以下几种方式来处理这样的场景。
1. 使用 @ResponseStatus 注解可以指定状态码;
2. 控制器方法可以返回 ResponseEntity 对象,该对象包含更多相关的元数据;
3. 异常处理器能够应对错误场景,这样处理器方法就能关注正常状况;
ResponseEntity 中包含了响应相关的元数据(头部信息和状态码)以及要转换成资源表述的对象。
所以当无法找到 User 对象的时候,我们可以返回 HTTP 404 错误。
@RequestMapping(value = "/{id}",method = RequestMethod.GET, produces = "application/json")
public ResponseEntity getUserById (@PathVariable("id") String id){
//初始化用户信息
initUserInfo();
User result = users.get(id);
HttpStatus status = result != null ? HttpStatus.OK : HttpStatus.NOT_FOUND;
return new ResponseEntity(result,status);
}
我们在正确的方向上走出了第一步,如果所要求的 User 无法找到,客户端就能就受到一个合适的状态码。但是在本例中,响应体依然为空。我们可能会希望在响应体中包含一些错误信息
首先定义一个包含错误信息的 Error 对象
public class Error {
private int code;
private String message;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
然后,我们可以修改 getById(), 让它返回 Error
@RequestMapping(value = "/{id}",method = RequestMethod.GET, produces = "application/json")
public ResponseEntity> getUserById (@PathVariable("id") String id){
//初始化用户信息
initUserInfo();
User result = users.get(id);
HttpStatus status = result != null ? HttpStatus.OK : HttpStatus.NOT_FOUND;
if(result == null){
Error error = new Error(4 , "User ("+id+") not found");
return new ResponseEntity(error, status);
}
return new ResponseEntity(result,status);
}
你可以发现,虽然问题的到了解决,但是代码却变得更加复杂,涉及到更多的逻辑,包括条件语句。另外,方法返回 ResponseEntity
public class UserNotFountException extends RuntimeException{
private String userId;
public UserNotFountException(String userId) {
this.userId = userId;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
}
接着,定义能够对应的 UserNotFountException 的错误处理器
@ExceptionHandler(UserNotFountException.class)
public ResponseEntity<Error> UserNotFound(UserNotFountException e){
String userId = e.getUserId();
Error error = new Error(4 , "User ("+userId+") not found");
return new ResponseEntity<Error>(error,HttpStatus.NOT_FOUND);
}
@ExceptionHandler 注解能够用到控制器方法中,用来处理特定的异常。
现在,我们可以修改 getUserById() 方法
@RequestMapping(value = "/{id}",method = RequestMethod.GET, produces = "application/json")
public ResponseEntity getUserById (@PathVariable("id") String id){
//初始化用户信息
initUserInfo();
User result = users.get(id);
HttpStatus status = result != null ? HttpStatus.OK : HttpStatus.NOT_FOUND;
if(result == null){
throw new UserNotFountException(id);
}
return new ResponseEntity(result,status);
}