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写博客一定要定时点击保存按钮。

可以使用Spring Boot中的JPA(Java Persistence API)实现将JSON数据写入MySQL数据库。JPA提供了一种简单的方式来管理对象关系映射(ORM),即将Java对象映射到关系型数据库中的表。 以下是一个简单的示例: 1. 创建一个实体类,用于映射JSON数据数据库表中的字段。 ```java @Entity @Table(name = "person") public class Person { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private Integer age; // getters and setters } ``` 2. 创建一个Spring Data JPA repository接口,用于定义CRUD操作。 ```java @Repository public interface PersonRepository extends JpaRepository<Person, Long> { } ``` 3. 在Controller中获取JSON数据,并将其转换为Person对象,然后使用PersonRepository将其保存到数据库中。 ```java @RestController @RequestMapping("/person") public class PersonController { @Autowired private PersonRepository personRepository; @PostMapping("/save") public void savePerson(@RequestBody Person person) { personRepository.save(person); } } ``` 这里假设JSON数据的格式与Person类的属性相匹配。如果不匹配,可以使用Jackson库进行转换。 另外,需要在application.properties文件中配置MySQL数据连接信息,例如: ``` spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase spring.datasource.username=myusername spring.datasource.password=mypassword spring.datasource.driver-class-name=com.mysql.jdbc.Driver ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值