SpringBoot构建RESTful API——处理返回异常

@ResponseBody 提供了一种很有用的方式,能够将控制器返回的 Java 对象转换为发送到客户端的资源表述。

一个好的 REST API 不仅仅能够在客户端和服务器之间传递资源,他还能够给客户端提供额外的数据,帮助客户端理解资源或者在请求中发生了什么情况。

发送错误信息到客户端

例如,我们为 UserController 中添加一个新的处理器方法,它会提供单个 Spittle 对象。

@RestController
@RequestMapping("/user")
public class UserController {

    Map<String, User> users = Collections.synchronizedMap(new HashMap<String, User>());

    @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

ResponseEntity 中包含了响应相关的元数据(头部信息和状态码)以及要转换成资源表述的对象。

添加状态码

所以当无法找到 User 对象的时候,我们可以返回 HTTP 404 错误。

@RequestMapping(value = "/{id}",method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<User> getUserById (@PathVariable("id") String id){
    //初始化用户信息
    initUserInfo();
    User result = users.get(id);
    HttpStatus status = result != null ? HttpStatus.OK : HttpStatus.NOT_FOUND;
    return new ResponseEntity<User>(result,status);
}

image_1b41g1c5d1gotg1tvf310d31bop9.png-7.4kB

我们在正确的方向上走出了第一步,如果所要求的 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>(error, status);
    }
    return new ResponseEntity<User>(result,status);
}

image_1b41gm2c6pdt17he19u01ji7p313.png-15.7kB

image_1b41gld31vq716urgscu8812jim.png-14.3kB

你可以发现,虽然问题的到了解决,但是代码却变得更加复杂,涉及到更多的逻辑,包括条件语句。另外,方法返回 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<User> 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<User>(result,status);
}

在响应中设置头部信息

展开阅读全文

没有更多推荐了,返回首页