Spring Security技术栈学习笔记(四)RESTful API服务异常处理

当我们从浏览器访问不存在的Spring BootRESTful API的时候,往往会返回Spring Boot内置的404错误界面,但是作为前后端分离的应用,相同的API也许会在其他终端访问,比如手机APP等,那么也会是相同的处理方式吗?

一、Spring Boot的默认处理方式分析
  • 从浏览器端访问

启动Spring Boot项目,从浏览器访问一个不存在的API,如“/user/hello”,这时候返回来的是一个HTML页面,如下图所示:
Spring Boot默认的404界面

  • 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.html500.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)
在这里插入图片描述

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值