当我们从浏览器访问不存在的
Spring Boot
的RESTful API
的时候,往往会返回Spring Boot
内置的404
错误界面,但是作为前后端分离的应用,相同的API
也许会在其他终端访问,比如手机APP
等,那么也会是相同的处理方式吗?
一、Spring Boot的默认处理方式分析
- 从浏览器端访问
启动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
返回的数据如下如所示:
这就是我们自定义的异常处理后的数据了。
Spring Security技术栈开发企业级认证与授权系列文章列表:
Spring Security技术栈学习笔记(一)环境搭建
Spring Security技术栈学习笔记(二)RESTful API详解
Spring Security技术栈学习笔记(三)表单校验以及自定义校验注解开发
Spring Security技术栈学习笔记(四)RESTful API服务异常处理
Spring Security技术栈学习笔记(五)使用Filter、Interceptor和AOP拦截REST服务
Spring Security技术栈学习笔记(六)使用REST方式处理文件服务
Spring Security技术栈学习笔记(七)使用Swagger自动生成API文档
Spring Security技术栈学习笔记(八)Spring Security的基本运行原理与个性化登录实现
Spring Security技术栈学习笔记(九)开发图形验证码接口
Spring Security技术栈学习笔记(十)开发记住我功能
Spring Security技术栈学习笔记(十一)开发短信验证码登录
Spring Security技术栈学习笔记(十二)将短信验证码验证方式集成到Spring Security
Spring Security技术栈学习笔记(十三)Spring Social集成第三方登录验证开发流程介绍
Spring Security技术栈学习笔记(十四)使用Spring Social集成QQ登录验证方式
Spring Security技术栈学习笔记(十五)解决Spring Social集成QQ登录后的注册问题
Spring Security技术栈学习笔记(十六)使用Spring Social集成微信登录验证方式
示例代码下载地址:
项目已经上传到码云,欢迎下载,内容所在文件夹为
chapter004
。
更多干货分享,欢迎关注我的微信公众号:爪哇论剑(微信号:itlemon)