先创建一个crud的项目。

controller调用service调用mapper

以下以简单代码代替

  • controller
    @GetMapping("/getUserById")
public String getUserById(String id){
String userById = userService.getUserById(id);
return userById;
}
  • service
    @Override
public String getUserById(String id) {
// 模拟业务
User user = userMapper.selectById(id);
return user.toString();
}

上面代码虽然是把数据返回给前台了,但是没有处理异常以及没有一个明确的标识告诉前端我是成功还是失败了,此时我们需要封装一下统一的成功失败标志来告诉前台如何处理返回数据。

使用Lombok 简单封装了一个CommonResponse类(lombok的使用需要考虑项目是否真正适合lombok,不要因为方便引入导致后期版本或者一些其他的隐藏坑。)

@Data
public class CommonResponse {
/**
* 返回业务码用来判断成功失败
* 200 成功
* 500 失败
*/
private String code; /** 描述 */
private String massage; /** 描述 */
private Object date; public CommonResponse(String code, String massage, Object date) {
this.code = code;
this.massage = massage;
this.date = date;
} public static CommonResponse succeed(){
return getCommonResponse(CodeEnum.SUCCESS.getCode(), CodeEnum.SUCCESS.getMassage(), null);
}
public static CommonResponse succeed(Object date){
return getCommonResponse(CodeEnum.SUCCESS.getCode(), CodeEnum.SUCCESS.getMassage(), date);
}
public static CommonResponse succeed(String massage,Object date){
return getCommonResponse(CodeEnum.SUCCESS.getCode(), massage, date);
} public static CommonResponse error(String massage){
return getCommonResponse(CodeEnum.ERROR.getCode(), massage, null);
}
public static CommonResponse error(String code,String massage){
return getCommonResponse(code, massage, null);
}
public static CommonResponse error(){
return getCommonResponse(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getMassage(), null);
} public static CommonResponse getCommonResponse(String code, String massage, Object date){
return new CommonResponse(code,massage,date);
}
}

返回的controller使用统一的CommonResponse

    @GetMapping("/getUserById")
public CommonResponse getUserById(String id){
String userById = userService.getUserById(id);
return CommonResponse.succeed(userById);
}

返回

{
"code": "200",
"massage": "成功",
"date": "User(id=1, username=嘉文00, password=1000000, age=5555)"
}

上述返回基本符合预期,但是当程序出现未知异常怎么办了。

对service改造下

    @Override
public String getUserByIdException(String id) {
User user = userMapper.selectById(id);
// 模拟业务异常
int i=5/0;
return user.toString();
}

controller 改造

@GetMapping("/getUserById")
public CommonResponse getUserById(String id){
try{
String userById = userService.getUserById(id);
return CommonResponse.succeed(userById);
}catch(Exception e){
e.printStackTrace();
log.error(e.getMessage());
return CommonResponse.error(e.getMessage());
}
}

上面是不是也可以,通过trycatch来判断如何返回。但是这样代码中就会出现大量的try catch,是不是很不好看,我们能不能添加一个统一的try呢,答案是可以的。

使用spring提供的统一的异常处理

spring 提供了三种异常捕获方式,个人比较推荐这一种

@Slf4j
@ControllerAdvice
public class ExceptionHandle {
/**
* 处理未知异常
* @param e
* @return
*/
@ExceptionHandler(Exception.class)
@ResponseBody
public CommonResponse handleException(Exception e){
log.error("系统异常:{}",e.getMessage());
return CommonResponse.error(e.getMessage());
} /**
* 处理主动抛出的自定义异常
* @param e
* @return
*/
@ExceptionHandler(BusinessException.class)
@ResponseBody
public CommonResponse handleBusinessException(BusinessException e){ log.error("自定义异常:{}",e.getErrMassage());
return CommonResponse.error(e.getErrCode(),e.getErrMassage());
}
}

踩过的坑

  1. 这个地方在写的时候遇到一个坑,因为捕获异常后的返回值是CommonResponse,所以要加上注解 @ResponseBody 便于 格式转换。
  2. 在配置@ExceptionHandler的时候不能配置两个相同的Exception。否则会不知道使用哪个而报错。

这时controller 已经很清晰了,如下:只用处理好业务调用,无需处理向上抛出的异常。

    @GetMapping("/getUserByIdException")
public CommonResponse getUserByIdException(String id){
String userById = userService.getUserByIdException(id);
return CommonResponse.succeed(userById);
}
@GetMapping("/getUserByIdBusinessException")
public CommonResponse getUserByIdBusinessException(String id){
String userById = userService.getUserByIdBusinessException(id);
return CommonResponse.succeed(userById);
}

当然有时候我们会遇到自己的校验不通过来终止程序。我们可以throw 一个Exception 或者我们需要定制返回码,自定义一个异常类也行。如下简单示例,大家可以根据自己的业务需求去自定义。

  • 自定义异常类BusinessException
@Data
public class BusinessException extends RuntimeException{ private static final long serialVersionUID = 918204099850898995L; private String errCode; private String errMassage; public BusinessException(String errCode,String errMassage){
super(errMassage);
this.errCode = errCode;
this.errMassage = errMassage;
}
}
  • service返回自定义异常
    @Override
public String getUserByIdBusinessException(String id) {
User user = userMapper.selectById(id);
// 模拟业务异常
if("1".equals(id)){
throw new BusinessException("400","id为1的数据不支持查询。");
}
return user.toString();
}

此时我们前端得到的返回

## 请求
http://localhost:8088/getUserByIdBusinessException?id=1
## 返回 {
"code": "400",
"massage": "id为1的数据不支持查询。",
"date": null
}

以上就是统一异常捕获跟统一的返回。

另外我们实际项目为了使业务异常好看,统一,我们可以定义一个枚举来存放我们的业务异常信息。

  1. 定义一个枚举类
public enum ExceptionEnum {

    BUSINESS_NOT_ONE("400","id为1的数据不支持查询"),
ERR_NOT_LOOK("401","长得太帅不让看")
// 往后面累加...
; private String code;
private String desc; ExceptionEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public void ThrowException(){
ThrowException(code,desc);
}
public void ThrowException(String errMassage){
errMassage = desc +":"+errMassage;
ThrowException(code,errMassage);
}
private BusinessException ThrowException(String code,String desc){
throw new BusinessException(code,desc);
}
}
  1. 在service 中抛出枚举异常
    @Override
public String getUserByIdBusinessExceptionByEnumOne(String id) {
User user = userMapper.selectById(id);
// 模拟业务异常
if("1".equals(id)){
ExceptionEnum.BUSINESS_NOT_ONE.ThrowException();
}
return user.toString();
}
@Override
public String getUserByIdBusinessExceptionByEnumTwo(String id) {
User user = userMapper.selectById(id);
// 模拟业务异常
if("look".equals(id)){
// 可以动态拼接异常信息
ExceptionEnum.ERR_NOT_LOOK.ThrowException("你说对吧"+id);
}
return user.toString();
}
  1. 前台返回

{
"code": "400",
"massage": "id为1的数据不支持查询",
"date": null
} {
"code": "401",
"massage": "长得太帅不让看:你说对吧look",
"date": null
}

这种做法的好处就是方便管理,一眼就知道自己项目中有多少错误,多少异常。但是也会有同学觉得这样写好费劲,每次抛异常的时候还要先在枚举类中定义。所以这个做法是项目跟成员而定。

以上是本人拙见,若有不合适之处,欢迎留言指正。

从项目中来到项目中去

Java项目常用的统一返回跟统一异常处理的更多相关文章

  1. java项目常用 BaseDao BaseService

    java项目常用 BaseDao BaseService IBaseDao 1 package com.glht.sim.dao; 2 3  import java.util.List; 4 5 6 ...

  2. Java基础学习总结(70)——开发Java项目常用的工具汇总

    要想全面了解java开发工具,我们首先需要先了解一下java程序的开发过程,通过这个过程我们能够了解到java开发都需要用到那些工具. 首先我们先了解完整项目开发过程,如图所示: 从上图中我们能看到一 ...

  3. Spring Boot 统一返回结果及异常处理

    在 Spring Boot 构建电商基础秒杀项目 (三) 通用的返回对象 & 异常处理 基础上优化.调整 一.通用类 1.1 通用的返回对象 public class CommonReturn ...

  4. java项目常用架构

    三层架构 : 界面层/表现层 UI 业务逻辑层 BLL 针对具体的问题的操作,也可以理解成对数据层的操作,对数据业务逻辑处理. 数据访问层 DAL 访问数据库 mvc : 而 MVC 是在三层架构的基 ...

  5. JAVA项目常用异常处理情况

    Java异常处理 网络整理 这里是异常的说明: 算术异常类:ArithmeticExecption 空指针异常类:NullPointerException 类型强制转换异常:ClassCastExce ...

  6. Java 项目创建 -- 统一结果处理、统一异常处理、统一日志处理

    一.IDEA 插件使用 1.说明 此处使用 SpringBoot 2.2.6 .JDK 1.8 .mysql 8.0.18 作为演示. 使用 IDEA 作为开发工具. 2.IDEA 插件 -- Lom ...

  7. [原创]Java项目统一UTC时间方案

    Java项目统一UTC时间方案 作者:Gods_巨蚁 引言 近期团队的个别项目在进行框架升级后,部分时间值存在8小时误差,原因是错误的将数据库中的时间数据理解成了UTC时间(旧版本认为是北京时间) 考 ...

  8. java统一返回标准类型

    一.前言.背景 在如今前后端分离的时代,后端已经由传统的返回view视图转变为返回json数据,此json数据可能包括返回状态.数据.信息等......因为程序猿的习惯不同所以返回json数据的格式也 ...

  9. Java封装接口统一返回数据模板

    现在大多数都使用前后端分离开发模式,前端通过Ajax请求访问后台服务器,后台返回JSON数据供前端操作,这里编写一个统一返回数据模板类,方便日后操作 public class R extends Ha ...

随机推荐

  1. CVE-2020-2883漏洞复现&&流量分析

    CVE-2020-2883漏洞复现&&流量分析 写在前面 网上大佬说CVE-2020-2883是CVE-2020-2555的绕过,下面就复现了抓包看看吧. 一.准备环境 靶机:win7 ...

  2. 微信小程序开发——后端Java(一)

    一.前言 最近接触了小程序的开发,后端选择Java,因为小程序的代码运行在腾讯的服务器上,而我们自己编写的Java代码运行在我们自己部署的服务器上,所以一开始不是很明白小程序如何与后台进行通信的,然后 ...

  3. Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile

    一.问题由来 下午的时候,电脑用得好好的,突然一下死机,么办法只能够重新启动.再次打开IDEA的时候,之前打开的所有的项目 信息都不在了,我重新打开项目,然后就出现问题,所有的类都报红了,这让我很是意 ...

  4. Dockerfile 多阶段构建实践

    写在前面 在Docker Engine 17.05 中引入了多阶段构建,以此降低构建复杂度,同时使缩小镜像尺寸更为简单.这篇小作文我们来学习一下如何编写实现多阶段构建的Dockerfile 关于doc ...

  5. 基于taro封装底下浮动弹窗组件

    先看效果图: jsx: import Taro, { Component } from '@tarojs/taro' import { View, Image } from '@tarojs/comp ...

  6. httpClient 下载

    private void button2_Click(object sender, EventArgs e) { get(); } private async Task get() { await D ...

  7. C++11 shared_ptr(智能指针)详解

    要确保用 new 动态分配的内存空间在程序的各条执行路径都能被释放是一件麻烦的事情.C++ 11 模板库的 <memory> 头文件中定义的智能指针,即 shared _ptr 模板,就是 ...

  8. C# lock的语法糖原理--《.net core 底层入门》之自旋锁,互斥锁,混合锁,读写锁

    在多线程环境中,多个线程可能会同时访问同一个资源,为了避免访问发生冲突,可以根据访问的复杂程度采取不同的措施 原子操作适用于简单的单个操作,无锁算法适用于相对简单的一连串操作,而线程锁适用于复杂的一连 ...

  9. Qt 中事件与处理

    一.事件与处理程序在运算过程中发生的一些事情:鼠标单击.键盘的按下...这些的事件的监控与处理在Qt中不是以信号的方式处理的.当这些事件发生时会调用QObject类中的功能函数(虚函数),所有的控件类 ...

  10. Javascript - Vue - 动画

    动画状态类名 vue动画通过将需要执行动画的标签放入transition标签中,再通过设置预置的vue动画类名的css样式来控制动画的呈现效果. 开场动画状态的三个类名 v-enter:动画开始之前的 ...