springboot2.0-统一处理返回结果和异常情况

本文介绍了Spring Boot项目中统一处理返回结果和异常处理的原因,给出了学生信息管理的案例,包括建表、配置pom.xml、创建实体类和Mapper等,还说明了controller层注解使用、异常抛出规则及返回结果注解作用,最后展示了不同输入的测试结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://blog.csdn.net/qq_31289187/article/details/82980714

一、统一处理返回结果和异常处理的原因:

1、在springboot项目里我们希望接口返回的数据包含至少三个属性:

a、code:请求接口的返回码,成功或者异常等返回编码,例如定义请求成功,code = "0000",查询结果为null,code = "0001";

b、msg:请求接口的描述,也就是对返回编码的描述,"0000":就表示请求成功,"0001":表示结果为null;

c、data:请求接口成功,返回的结果。

    {
        "data": {
            "id": 1,
            "studentId": "13240115",
            "name": "Tiger",
            "age": 25,
            "famillyAddress": "北京",
            "createdDate": "2018-10-08T05:45:49.000+0000",
            "updatedDate": "2018-10-09T03:15:33.000+0000"
        },
        "code": "0000",
        "msg": "请求成功"
    }

2、在springboot项目里我们希望请求结果失败之后,通过返回码和返回描述来告诉前端接口请求异常。

    {
        "code": "0001",
        "msg": "学号不存在"
    }

二、案例

1、建一张学生信息表,包含学生的学号、姓名、年龄、家庭住址等

    CREATE TABLE student_info (
      id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增',
      student_id varchar(20) NOT NULL COMMENT '学号',
      name varchar(64) NOT NULL COMMENT '姓名',
      age int(2) NOT NULL COMMENT '年龄',
      familly_address varchar(256) NOT NULL COMMENT '家庭地址',
      created_date datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      updated_date datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
      PRIMARY KEY (student_id),
      KEY id (id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

2、pom.xml

    <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
     
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
     
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
     
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.3.1</version>
            </dependency>
     
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.46</version>
            </dependency>
     
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.9</version>
            </dependency>
     
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.16.22</version>
            </dependency>
     
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.43</version>
            </dependency>
     
     
            <dependency>
                <groupId>com.squareup.okhttp3</groupId>
                <artifactId>okhttp</artifactId>
                <version>3.9.1</version>
            </dependency>
     
            <dependency>
                <groupId>org.glassfish</groupId>
                <artifactId>javax.json</artifactId>
                <version>1.0.4</version>
            </dependency>
     
            <dependency>
                <groupId>org.apache.tomcat.embed</groupId>
                <artifactId>tomcat-embed-jasper</artifactId>
            </dependency>
     
        </dependencies>

3、案例中使用redis进行缓存,可以不需要

Windows环境安装redis以及缓存应用

4、创建实体类:StudentInfo

    package com.dl.cn.message.bean;
     
    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Data;
    import lombok.NoArgsConstructor;
     
    import java.io.Serializable;
    import java.util.Date;
     
    /**
     * Created by Tiger on 2018/10/8.
     */
    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public class StudentInfo implements Serializable{
     
        private static final long serialVersionUID = 2597547944454691103L;
     
        private Long id;
        private String studentId;
        private String name;
        private Integer age;
        private String famillyAddress;
        private Date createdDate;
        private Date updatedDate;
    }

5、创建Mapper:StudentInfoMapper

    package com.dl.cn.message.mapper;
     
    import com.dl.cn.message.bean.StudentInfo;
    import org.apache.ibatis.annotations.*;
     
    /**
     * Created by Tiger on 2018/10/8.
     */
    @Mapper
    public interface StudentInfoMapper {
        @Insert("insert into student_info(student_id,name,age,familly_address)" +
                " values(#{studentId},#{name},#{age},#{famillyAddress})")
        /**
         * 通过bean保存实体类是,建议不要通过@Param注解,负责实体类的属性都在@Param中找
         * */
        void saveStudentInfo(StudentInfo studentInfo);
     
     
        @Select("select * from student_info where student_id = #{studentId}")
        StudentInfo findByStudentId(@Param("studentId") String studentId);
     
     
        @Update("update student_info set familly_address = #{famillyAddress},updated_date = now() ")
        void updateFamillyAddress(@Param("studentId") String studentId,@Param("famillyAddress") String famillyAddress);
    }

6、创建service:StudentInfoService

    package com.dl.cn.message.service;
     
    import com.dl.cn.message.bean.StudentInfo;
    import com.dl.cn.message.mapper.StudentInfoMapper;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cache.annotation.CacheConfig;
    import org.springframework.cache.annotation.CacheEvict;
    import org.springframework.cache.annotation.Cacheable;
    import org.springframework.stereotype.Service;
     
    /**
     * Created by Tiger on 2018/10/8.
     */
    @Service
    @CacheConfig(cacheNames = "studentInfo")
    @Slf4j
    public class StudentInfoService {
     
        @Autowired
        StudentInfoMapper studentInfoMapper;
     
        /**
         * 保存学生信息
         * @param studentInfo
         * */
        public void saveStudentInfo(StudentInfo studentInfo){
            studentInfoMapper.saveStudentInfo(studentInfo);
        }
     
        /**
         * 根据学号查学生信息
         * @param studentId
         * @return
         * */
        @Cacheable(key = "#studentId",unless = "#result == null")
        public StudentInfo findByStudentId(String studentId){
            log.info("查找信息:{}",studentId);
            return studentInfoMapper.findByStudentId(studentId);
        }
     
        /**
         * 根据学号更新家庭地址
         * @param studentId
         * @param famillyAddress
         * */
        //删除对应key的缓存
        @CacheEvict(key = "#studentId")
        public void updateFamillyAddress(String studentId,String famillyAddress){
            studentInfoMapper.updateFamillyAddress(studentId,famillyAddress);
        }
    }

7、创建统一返回结果类:Response

    package com.dl.cn.message.response;
     
    import com.fasterxml.jackson.databind.annotation.JsonSerialize;
    import lombok.Getter;
    import lombok.Setter;
     
    import java.io.Serializable;
     
    /**
     * 请求返回类
     * Created by Tiger on 2018/10/9.
     */
    @Getter
    @Setter
    @JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)
    public class Response<T> implements Serializable {
     
        private static final long serialVersionUID = -4505655308965878999L;
     
        //请求成功返回码为:0000
        private static final String successCode = "0000";
        //返回数据
        private T data;
        //返回码
        private String code;
        //返回描述
        private String msg;
     
        public Response(){
            this.code = successCode;
            this.msg = "请求成功";
        }
     
        public Response(String code,String msg){
            this();
            this.code = code;
            this.msg = msg;
        }
        public Response(String code,String msg,T data){
            this();
            this.code = code;
            this.msg = msg;
            this.data = data;
        }
        public Response(T data){
            this();
            this.data = data;
        }
    }

8、创建异常编码和描述类:ErrorCodeAndMsg

    package com.dl.cn.message.enums;
     
    /**
     * Created by Tiger on 2018/10/9.
     */
    public enum  ErrorCodeAndMsg {
     
        Student_number_does_not_exist("0001","学号不存在"),
        Insufficient_student_number("0002","学号长度不足"),
        Student_number_is_empty("0003","学号为空"),
        Network_error("9999","网络错误,待会重试"),
        ;
     
        private String code;
        private String msg;
     
        ErrorCodeAndMsg(String code, String msg) {
            this.code = code;
            this.msg = msg;
        }
     
        public String getCode() {
            return code;
        }
        public void setCode(String code) {
            this.code = code;
        }
        public String getMsg() {
            return msg;
        }
        public void setMsg(String msg) {
            this.msg = msg;
        }
    }

9、创建统一异常处理类:StudentException

    package com.dl.cn.message.exception;
     
    import com.dl.cn.message.enums.ErrorCodeAndMsg;
     
    import java.io.Serializable;
     
    /**
     * 统一异常捕获类
     * Created by Tiger on 2018/10/9.
     */
    public class StudentException extends RuntimeException{
     
        private static final long serialVersionUID = -6370612186038915645L;
     
        private final ErrorCodeAndMsg response;
     
        public StudentException(ErrorCodeAndMsg response) {
            this.response = response;
        }
        public ErrorCodeAndMsg getResponse() {
            return response;
        }
    }

10、创建异常处理的全局配置类:ExceptionHandler

    package com.dl.cn.message.exception;
     
    import com.dl.cn.message.enums.ErrorCodeAndMsg;
    import com.dl.cn.message.response.Response;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ResponseBody;
     
    import javax.servlet.http.HttpServletRequest;
     
    /**
     * Created by Tiger on 2018/10/9.
     */
    @ControllerAdvice
    @Slf4j
    public class ExceptionHandler {
        @org.springframework.web.bind.annotation.ExceptionHandler(StudentException.class)
        @ResponseBody
        public Response handleStudentException(HttpServletRequest request, StudentException ex) {
            Response response;
            log.error("StudentException code:{},msg:{}",ex.getResponse().getCode(),ex.getResponse().getMsg());
            response = new Response(ex.getResponse().getCode(),ex.getResponse().getMsg());
            return response;
        }
        @org.springframework.web.bind.annotation.ExceptionHandler(Exception.class)
        @ResponseBody
        public Response handleException(HttpServletRequest request, Exception ex) {
            Response response;
            log.error("exception error:{}",ex);
            response = new Response(ErrorCodeAndMsg.Network_error.getCode(),
                    ErrorCodeAndMsg.Network_error.getMsg());
            return response;
        }
    }

11、创建controler类:StudentInofController

    package com.dl.cn.message.controller;
     
    import com.dl.cn.message.enums.ErrorCodeAndMsg;
    import com.dl.cn.message.exception.StudentException;
    import com.dl.cn.message.response.Response;
    import com.dl.cn.message.service.StudentInfoService;
    import com.dl.cn.message.bean.StudentInfo;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
     
    /**
     * Created by Tiger on 2018/10/8.
     */
    @RestController
    @RequestMapping("/student")
    @Slf4j
    public class StudentInofController {
        @Autowired
        StudentInfoService studentInfoService;
     
        /**
         * 保存学生信息
         * @param studentId
         * @param name
         * @param age
         * @param famillyAddress
         * */
        @PostMapping("/save")
        public void saveStudentInfo(@RequestParam("student_id") String studentId,
                                    @RequestParam("name") String name,
                                    @RequestParam("age") Integer age,
                                    @RequestParam("familly_address") String famillyAddress){
            StudentInfo studentInfo = StudentInfo.builder()
                    .studentId(studentId)
                    .name(name)
                    .age(age)
                    .famillyAddress(famillyAddress)
                    .build();
            studentInfoService.saveStudentInfo(studentInfo);
        }
     
        /**
         * 根据学号查学生信息
         * @param studentId
         * @return
         * */
        @PostMapping("/findByStudentId")
        public Response findByStudentId(@RequestParam("student_id") String studentId){
            try{
                log.info("Get student information based on student number:{}",studentId);
                if(studentId == null){
                    throw new StudentException(ErrorCodeAndMsg.Student_number_is_empty);
                }
                //学号固定为8位
                if(studentId.length() != 8){
                    throw new StudentException(ErrorCodeAndMsg.Insufficient_student_number);
                }
                StudentInfo studentInfo = studentInfoService.findByStudentId(studentId);
                if(studentInfo == null){
                    throw new StudentException(ErrorCodeAndMsg.Student_number_does_not_exist);
                }
                return new Response(studentInfo);
            }catch (Exception e){
                if(e instanceof StudentException){
                    throw e;
                }else {
                    log.error("findByStudentId error:",e);
                    throw new StudentException(ErrorCodeAndMsg.Network_error);
                }
            }
        }
     
        @PostMapping("/updateFamillyAddress")
        public Response updateFamillyAddress(@RequestParam("student_id") String studentId,
                                         @RequestParam("familly_address") String famillyAddress){
            studentInfoService.updateFamillyAddress(studentId,famillyAddress);
            Response response = new Response();
            System.out.println(response.toString());
            return response;
        }
     
     
    }

12、application.properties配置

    #redis
    spring.redis.host=127.0.0.1
    spring.redis.port=6379
    spring.redis.password=tiger
     
    #mybatis
    #开启mybatis驼峰命名,这样可以将mysql中带有下划线的映射成驼峰命名的字段
    mybatis.configuration.map-underscore-to-camel-case=true
     
    #datasource
    spring.datasource.url=jdbc:mysql://127.0.0.1:3306/tiger?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&generateSimpleParameterMetadata=true
    spring.datasource.username=tiger
    spring.datasource.password=tiger
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    spring.datasource.max-idle=10
    spring.datasource.max-wait=60000
    spring.datasource.min-idle=5
    spring.datasource.initial-size=5
    spring.datasource.validationQuery=select 'x'

三、说明

1、controller层使用注解@RestController,这样返回结果就是json格式,而@Controller返回结果是字符串

2、throw 异常

如果exception类型是自定义的异常StudentException,直接抛出,如果是其它异常统一抛出网络错误

    try{
     
    }catch (Exception e){
                if(e instanceof StudentException){
                    throw e;
                }else {
                    log.error("findByStudentId error:",e);
                    throw new StudentException(ErrorCodeAndMsg.Network_error);
           }
    }

3、在返回结果类添加了注解@JsonSerialize(include= JsonSerialize.Inclusion.NON_NULL)

是因为更新或者删除操作,一般没有返回值,我只需要知道是否更新成功或者删除成功就OK了,如果不加这个注解

我们返回的结果中data为null!!!

    {
        "data": null,
        "code": "0000",
        "msg": "请求成功"
    }

加上注解再更新数据,返回结果:

    {
        "code": "0000",
        "msg": "请求成功"
    }

因此这个注解的作用就是:返回结果中有null值,干掉它!

四、测试结果

mysql数据库中有一条学号为13240115的数据:

1、student_id = "13240115"时

    {
        "data": {
            "id": 1,
            "studentId": "13240115",
            "name": "Tiger",
            "age": 25,
            "famillyAddress": "北京",
            "createdDate": "2018-10-08T05:45:49.000+0000",
            "updatedDate": "2018-10-09T05:36:36.000+0000"
        },
        "code": "0000",
        "msg": "请求成功"
    }

2、student_id = "13240114"时

    {
        "code": "0001",
        "msg": "学号不存在"
    }

3、student_id = "1324011",不足8位时

    {
        "code": "0002",
        "msg": "学号长度不足"
    }

4、student_id = "13240115",然后在接口中加上一行代码,System.out.println(1/0);

返回结果:

    {
        "code": "9999",
        "msg": "网络错误,待会重试"
    }

控制台日志:

    java.lang.ArithmeticException: / by zero
        at com.dl.cn.message.controller.StudentInofController.findByStudentId(StudentInofController.java:54) ~[classes/:na]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_161]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_161]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_161]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_161]
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) [spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]

通过测试,发现这个小案例满足刚开始我们提出的需求,一定还有很多其它问题,暂时没有发现,我会及时修改,不知道有人是否看我的博客?我只是想把自己的学习成果总结记录下来。人可以成长为芳草,也可以长成杂莠!!!
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值