Spring Boot后端返还JSON数据 + 统一异常处理(返还JSON错误)
1 背景
现在项目都是前后端分离,前后端通过JSON交换数据,这时,后端就需要提供健全的api接口供前端调用。本文主要介绍自己封装的JsonResult与Spring Boot的统一异常处理,项目地址在这里。
2 JsonResult
JsonResult是自己封装的一个返还Json,主要由返还码、返还信息,以及返还数据构成,其中提供了静态方法success、failed可供controller层直接调用。废话不多说,直接上代码供大家参考。
package com.lm.common; import org.apache.commons.lang3.builder.ToStringBuilder; import java.io.Serializable; import java.util.Objects; /** * @author lming.41032@gmail.com * @date 18-11-7 下午12:25 */ public class JsonResult implements Serializable { private static final long serialVersionUID = 5598308977637490090L; private int status; private String message; private Object data; public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public void setData(Object data) { this.data = data; } public Object getData() { return data; } public static JsonResult success() { return success(null); } public static JsonResult success(Object data) { JsonResult jsonResult = new JsonResult(); jsonResult.setStatus(200); jsonResult.setMessage("SUCCESS"); if (data != null) { jsonResult.setData(data); } return jsonResult; } public static JsonResult failed() { return failed("FAILED"); } public static JsonResult failed(String message) { JsonResult jsonResult = new JsonResult(); jsonResult.setStatus(500); jsonResult.setMessage(message); return jsonResult; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof JsonResult)) { return false; } JsonResult result = (JsonResult) o; return status == result.status && Objects.equals(message, result.message) && Objects.equals(data, result.data); } @Override public int hashCode() { return Objects.hash(status, message, data); } @Override public String toString() { return new ToStringBuilder(this) .append("status", status) .append("message", message) .append("data", data) .toString(); } }
以下就是一个模拟登录成功返还的Json信息.
{ "status":200, "message":"SUCCESS", "data": { "id":1, "userName":"lmm", "password":"123" } }
3 统一异常处理(返还Json)
在前后端分离项目中,无论后台程序出现了什么问题,我们都要返还一个错误的Json信息。这个错误的信息可以包括错误码,错误信息等,这样不仅仅方便前端处理,也方便后期运维、开发人员快速的定位到错误。下面就是一个错误的Json信息。
{ "status":700, "message":"用户不存在!", "data":null }
首先我们需要定义一个基本异常,它主要包包含错误码,错误信息。我们也可以在这个基本异常中预先定义一些异常如参数错误、没有权限等。
package com.lm.common; /** * @author lming.41032@gmail.com * @date 18-11-7 上午11:46 */ public class BaseException extends Exception { private static final long serialVersionUID = -3392027101671055457L; public static final int ERROR_CODE_ILLEGAL_ARGUMENTS = 600; // 错误码 private int code; //错误信息 private String errorMessage; public BaseException(int code) { super("error code" + code); this.code = code; } public BaseException(int code, Throwable throwable) { super(throwable); this.code = code; } public BaseException(int code, String errorMessage) { super(errorMessage); this.code = code; this.errorMessage = errorMessage; } public int getCode() { return code; } public String getErrorMessage() { return errorMessage; } }
一个系统由多个模块组成,每个模块中都可能会抛出各种各样的异常,这时我们就需要在模块中定义属于本模块中的异常,这些异常需继承BaseException。比如用户登录模块,可能就会抛出用户不存在、密码错误等异常,这时我们定义一个UserException异常,如下所示。
package com.lm.user; import com.lm.common.BaseException; /** * @author lming.41032@gmail.com * @date 18-11-7 上午11:53 */ public class UserException extends BaseException { public static final int ERROR_CODE_NOT_EXIT = 700; public static final int ERROR_CODE_PASSWORD_EEEOR = 701; public UserException(int code) { super(code); } }
定义完异常后,我们需要对异常进行处理,这样后台抛出异常后,将直接返还错误Json。我们定义一个统一异常处理类(GlobalExceptionHandle),用@ControllerAdvice注解该类,用注解@ExceptionHandler来定义要处理的异常类型。用@ResponseBody来使返还的数据为Json格式。
package com.lm.dubboconsumer.common; import com.google.gson.Gson; import com.lm.common.BaseException; import com.lm.common.JsonResult; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.context.NoSuchMessageException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.UndeclaredThrowableException; import java.util.Locale; /** * @author lming.41032@gmail.com * @date 18-11-7 下午12:17 */ @ControllerAdvice public class GlobalExceptionHandle { private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandle.class); private final MessageSource messageSource; @Autowired public GlobalExceptionHandle(MessageSource messageSource) { this.messageSource = messageSource; } @ExceptionHandler(value = Exception.class) @ResponseBody public JsonResult defaultErrorHandle(HttpServletRequest request, HttpServletResponse response, Exception e, Locale locale) throws IOException { JsonResult result = new JsonResult(); int code = 500; String errorMessage = null; Throwable exception = e; if (exception instanceof BaseException) { code = ((BaseException) exception).getCode(); errorMessage = ((BaseException) exception).getErrorMessage(); } else if (exception instanceof MissingServletRequestParameterException) { code = BaseException.ERROR_CODE_ILLEGAL_ARGUMENTS; } else if (!(exception instanceof BaseException || exception instanceof MissingServletRequestParameterException)) { LOGGER.error("Unknown Exception, URI = " + request.getRequestURI(), e); } else { LOGGER.error("URI = {}, errorCode = {}, errorMessage = {}", request.getRequestURI(), code, errorMessage); } // 如果异常中包含了错误信息,则不会通过错误码获取定义的错误信息 if (StringUtils.isBlank(errorMessage)) { String prefix = exception.getClass().getSimpleName(); errorMessage = getMessage(prefix + "." + code, locale); if (errorMessage == null) { errorMessage = getMessage(Integer.toString(code), locale); } } result.setStatus(code); result.setMessage(errorMessage); return result; } private String getMessage(String key, Locale locale) { String errorMessage = null; try { errorMessage = messageSource.getMessage(key, null, locale); } catch (NoSuchMessageException exception) { LOGGER.debug("ErrorMessage|NotFound|{}", key); } return errorMessage; } }
我们前面定义的异常都是都是只定义了错误码,至于错误信息,我们可以在项目resource下建一个error_codes.properties文件,其中=前面是我们自定义异常中的code,而=号右边的就是错误信息了。
500=系统错误,请稍后重试! 600=参数错误! 700=用户不存在! 701=用户已经存在!
error_codes.properties文件创建完后,需要在aplication.yml(aplication.properties)中设置一下。
spring: messages: basename: error_codes cache-duration: 600s
4 测试
Service层
package com.lm.dubboprovider.user.impl; import com.lm.common.BaseException; import com.lm.user.User; import com.lm.user.UserException; import com.lm.user.service.UserService; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; /** * @author lming.41032@gmail.com * @date 18-11-4 下午2:55 */ @Service public class UserServiceImpl implements UserService { private static final String USERNAME = "lm"; private static final String PASSWORD = "123"; @Override public Boolean login(String userName, String password) throws BaseException { if (StringUtils.isBlank(userName) || StringUtils.isBlank(password)) { throw new BaseException(BaseException.ERROR_CODE_ILLEGAL_ARGUMENTS); } if (!userName.equals(USERNAME)) { throw new UserException(UserException.ERROR_CODE_NOT_EXIT); } if (USERNAME.equals(userName) && PASSWORD.equals(password)) { return true; } return false; } }
Controller层
package com.lm.dubboconsumer.user; import com.lm.common.BaseException; import com.lm.common.JsonResult; import com.lm.user.User; import com.lm.user.UserException; import com.lm.user.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; /** * @author lming.41032@gmail.com * @date 18-11-5 下午5:10 */ @RestController @RequestMapping("user") public class UserRestController { private final UserService userService; @Autowired public UserRestController(UserService userService) { this.userService = userService; } @GetMapping("login") public JsonResult login(HttpServletRequest request) throws UserException, BaseException { String userName = request.getParameter("username"); String password = request.getParameter("password"); Boolean flag = userService.login(userName, password); if (flag) { return JsonResult.success(); } else { throw JsonResult.failed(); } } }
登录成功:
参数错误:
用户不存在:
5 总结
CSDN写博客一定要定时点击保存按钮。
- 【SpringBoot】Http请求统一异常(返回数据)处理与单元测试
- SpringBoot进阶之使用异常替代返回错误码(拦截异常并统一处理)
- Spring Boot HTTP over JSON 的错误码异常处理
- Spring Boot HTTP over JSON 的错误码异常处理
- spring boot之统一错误异常处理
- spring-boot统一处理返回给前台的数据格式(避免返回异常堆栈信息,干扰用户体验)
- Spring Boot HTTP over JSON 的错误码异常处理
- SpringBoot系列五:SpringBoot错误处理(数据验证、处理错误页、全局异常)
- springboot json 异常统一处理,自定义异常处理
- springBoot的返回code数据的处理以及统一异常处理
- SpringCloud SpringBoot mybatis 分布式微服务(六)Spring Boot中Web应用的统一异常处理
- SpringBoot框架统一异常处理
- SpringBoot学习之全局异常处理设置(返回JSON)
- SpringBoot学习——全局异常处理设置(返回JSON)
- 解决spring boot中rest接口404 500等错误返回统一的json格式
- 解决spring boot中rest接口404,500等错误返回统一的json格式
- Spring Boot中使用AOP统一处理web层异常的方法
- Spring Boot中Web应用的统一异常处理
- Spring Cloud Spring Boot mybatis分布式微服务云架构(十一)Web应用的统一异常处理
- STS创建Spring Boot项目实战(Rest接口、数据库、用户认证、分布式Token JWT、Redis操作、日志和统一异常处理)