当我们从浏览器访问不存在的
Spring Boot
的RESTful API
的时候,往往会返回Spring Boot
内置的404
错误界面,但是作为前后端分离的应用,相同的API
也许会在其他终端访问,比如手机APP
等,那么也会是相同的处理方式吗?
启动Spring Boot
项目,从浏览器访问一个不存在的API
,如“/user/hello
”,这时候返回来的是一个HTML
页面,如下图所示:
APP
端访问从APP
端访问我们可以使用模拟RESTful API
发送器来进行发送,我这里使用的Paw
软件,你可以在你的谷歌浏览器上安装Postman
来进行发送。访问“/user/hello
”返回的结果如下如所示:
对比上面的两种访问方式,返回的错误类型是不一样的,浏览器访问返回的是一个HTML
页面,而客户端访问返回的是一个JSON
数据。那么问题来了,Spring Boot
是如何确定当前请求来自浏览器还是客户端?我们可以从它的源代码中找到答案。
这段源代码来自于Spring Boot
的一个包org.springframework.boot.autoconfigure.web
中的BasicErrorController
,从类名就可以知道它是一个Controller
,且处理的路径就是“/error
”。找到错误处理的两个方法,如下图所示:
第一个方法和第二个方法处理的都是同一个API
,区别就在于第一个方法的@RequestMapping
里面包含一个produces
属性,它表示将生成什么类型的资源返回给前端,很明显,第一个方法要返回的是一个HTML
页面,而第二个方法返回的是JSON
数据。这就很明了了,当浏览器访问错误的API
的时候,会自动进入第一个方法处理错误,从客户端访问的时候,就会进入第二个方法处理错误。当然,从浏览器发送的请求的时候,我们可以看见请求头中看到浏览器要求的返回数据类型就包含了text/html
,如下图所示:
以上的例子都是访问资源不存在的案例,访问的处理逻辑并未进入对应的Controller
就被Spring Boot
打回去了,如果是服务代码抛出了异常,Spring Boot
是如何处理的呢?在这里我再写一个Controller
,手动抛出异常。代码如下:
@GetMapping("/user5")
@ResponseBody
public User user5() {
throw new RuntimeException("User is not exist.");
}
从浏览器端访问http://localhost:8080/user5
,返回的依然是HTML
页面,如下所示:
而从客户端访问,返回的依然是JSON
数据,如下图所示:
在实际的开发过程中,如果出现404
或者500
的错误的时候,返回给浏览器是Spring Boot
默认的处理界面,这并不友好,我们可以实现自定义页面来给出更好的温馨提示。
这里仅仅介绍一种最简单的方式来处理异常,在resources
文件夹下再建立一个resources
文件夹,然后再在新建的resources
文件夹下建立一个error
,在error
文件夹里面建立404.html
和500.html
,在访问出现404
错误的时候,就会跳转到我们自己定义的HTML
中,而不是Spring Boot
默认的界面。
package com.lemon.security.web.exception;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author lemon
* @date 2018/4/1 下午2:08
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class UserNotExistException extends RuntimeException {
private Integer id;
public UserNotExistException(Integer id) {
super("user not exist.");
this.id = id;
}
}
为了测试,在Controller
添加一个方法,代码如下:
@GetMapping("/user6/{id:\\d+}")
@ResponseBody
public User user6(@PathVariable Integer id) {
throw new UserNotExistException(id);
}
当访问这个API
的时候,就会抛出我们自定义的异常,这时候,Spring Boot
默认的处理方式返回的结果如下图:
有时候我们前端不需要这么多的信息,只需要部分信息,这个时候就需要自定义异常处理了,而不是采用Spring Boot
的默认处理方式了,在这里,我们可以写一个异常处理类,专门用来处理自定义异常。
package com.lemon.security.web.exception.handler;
import com.lemon.security.web.exception.UserNotExistException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import java.util.HashMap;
import java.util.Map;
/**
* 自定义错误处理逻辑
*
* @author lemon
* @date 2018/4/1 下午2:48
*/
@ControllerAdvice
public class UserNotExistExceptionHandler {
@ExceptionHandler(UserNotExistException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public Map<String, Object> handleUserNotExistException(UserNotExistException ex) {
Map<String, Object> result = new HashMap<>();
result.put("id", ex.getId());
result.put("message", ex.getMessage());
return result;
}
}
当一个类加上了@ControllerAdvice
注解,那么这个类就具备了处理其他Controller
异常的能力,具体的处理方式还是通过方法来进行的。上面的方法就是专门来处理UserNotExistException
这个异常的,@ExceptionHandler
就是指定了需要被处理的异常,@ResponseStatus
指定状态码,最后将处理后的数据返回。定义好这个类之后,当代码中抛出了UserNotExistException
异常的时候,都会转到这个方法中进行处理。再次运行应用,访问http://localhost:8080/user6/1
返回的数据如下如所示:
这就是我们自定义的异常处理后的数据了。