使用Spring框架自带对象,0入侵统一正常或异常接口对象返回

背景

公司的一个项目,没有统一接口返回值。前端对接接口时,由于不同接口返回对象的千差万别,导致抽象处理接口返回结果很复杂。
有的公司,会新增一个统一返回对象,每一个接口,都返回这个对象。这样是可以实现的,很多公司也是这么做的,但是对代码有一定的入侵性。
这里用另一个思路来实现:

  • 程序异常返回时,通过异常拦截,捕捉异常来构造统一返回对象
  • 程序正常返回时,自定义返回值处理器来实现统一处理返回值的功能。

对于一个新手来说,接口返回值很随意,下面这样写接口是很常见的:


    @RequestMapping("getAll")
    public List<GoodJobEveryDay> getAll() throws JsonProcessingException {

        List<GoodJobEveryDay> value = new ArrayList<>();
        // 判断文件是否存在
        if (FileHandling.isFileExists(FileHandling.FILE_NAME)) {
            // 如果文件存在,读取文件内容
            String fileContent = FileHandling.readFileContent(FileHandling.FILE_NAME);
            value = objectMapper.readValue(fileContent, new TypeReference<List<GoodJobEveryDay>>() {
            });
        }
        return value;
    }


    @GetMapping("getOne")
    public GoodJobEveryDay getOne(@RequestParam Integer id) throws JsonProcessingException {
        List<GoodJobEveryDay> value = getAll();
        if (!CollectionUtils.isEmpty(value)) {
            if (value.stream().filter(it -> id.equals(it.getId())).count() <= 0) {
                throw new BusinessException(ExceptionEnum.DATA_NOT_FOUND);
            }
            Optional<GoodJobEveryDay> first = value.stream().filter(it -> it.getId().equals(id)).findFirst();
            return first.isPresent() ? first.get() : null ;
        }
        return null;
    }

那么,怎么才能达到以下结果果:
正常返回:

## 正常返回对象
{
	"headers": {},
	"body": {
		"id": 1,
		"content": "做了五组运动减大肚子;开始了记录每天做得好的地方。",
		"badContent": null,
		"time": "2024-02-18"
	},
	"statusCodeValue": 200,
	"statusCode": "OK"
}
## 正常返回数组
{
	"headers": {},
	"body": [
		{
			"id": 1,
			"content": "做了五组运动减大肚子;开始了记录每天做得好的地方。",
			"badContent": null,
			"time": "2024-02-18"
		},
		{
			"id": 2,
			"content": "早上6:20起床做饭;做了五组运动减大肚子。",
			"badContent": "晚上边做运动的间隙,喝了一瓶可乐!",
			"time": "2024-02-19"
		}
	],
	"statusCode": "OK",
	"statusCodeValue": 200
}

异常返回:

## 程序框架 抛出的异常
{
	"headers": {},
	"body": {
		"message": "Required Integer parameter 'id' is not present",
		"code": 500
	},
	"statusCode": "INTERNAL_SERVER_ERROR",
	"statusCodeValue": 500
}

## 业务代码主动 抛出的异常,前端可以根据code来做适当处理
{
	"headers": {},
	"body": {
		"message": "数据不存在",
		"code": 100002
	},
	"statusCodeValue": 500,
	"statusCode": "INTERNAL_SERVER_ERROR"
	}

条件

博主写代码时,是基于 spring-web-5.2.6.RELEASE 版本。

核心处理代码

  1. @RestControllerAdvice + @ExceptionHandler 即可实现异常拦截,从而进行异常统一处理,返回 ErrorResponse 自定义异常对象。
  2. 通过 ResponseBodyAdvice<Object> 来实现返回值统一处理,重写beforeBodyWrite方法,程序返回之前,将正常返回和异常返回统一处理。

import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * 自定义返回值处理器 来实现统一处理返回值的功能。
 */
@RestControllerAdvice
public class ResponseBodyHandler implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 支持所有类型的返回值
        return true;
    }

    /**
     * 正常或异常结果返回前处理 body
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
                                  ServerHttpResponse response) {
        // 业务自定义错误,前端可以根据请求体body中错误码code来做对应逻辑
        if (body instanceof ErrorResponse) {
            return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
        } else if (body instanceof ResponseEntity) {
            // 如果返回值已经是ResponseEntity类型,则不做处理
            return body;
        }
        // 否则包装成ApiResponse对象
        return ResponseEntity.ok(body);
    }


    /**
     * 异常拦截,处理异常返回对象
     * @param ex 异常信息
     * @return ResponseEntity<ErrorResponse>
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleException(Exception ex) {
        if (ex instanceof BusinessException) {
            BusinessException exception = (BusinessException) ex;
            return new ResponseEntity<>(new ErrorResponse(exception.getMessage(), exception.getCode()), HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return new ResponseEntity<>(new ErrorResponse(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value()), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

附录代码

自定义异常返回类 ErrorResponse

public class ErrorResponse {
    private String message;
    private int code;

    public ErrorResponse() {
    }

    public ErrorResponse(String message, int code) {
        this.message = message;
        this.code = code;
    }
    public ErrorResponse(ExceptionEnum exceptionEnum) {
        this.message = exceptionEnum.getMessage();
        this.code = exceptionEnum.getCode();
    }

    // Getters and setters
}

自定义业务异常 BusinessException

public class BusinessException extends RuntimeException {
    private int code;

    public BusinessException(int code) {
        super();
        this.code = code;
    }

    public BusinessException() {
        super();
    }

    public BusinessException(String message, int code) {
        super(message);
        this.code = code;
    }
    public BusinessException(ExceptionEnum exceptionEnum) {
        super(exceptionEnum.getMessage());
        this.code = exceptionEnum.getCode();
    }

    public BusinessException(String message, Throwable cause, int code) {
        super(message, cause);
        this.code = code;
    }

    public BusinessException(Throwable cause, int code) {
        super(cause);
        this.code = code;
    }

    public int getCode() {
        return code;
    }
}

自定义系统业务异常码

/**
 * 业务错误码
 */
public enum ExceptionEnum {
    REPEAT_LOGIN_NAME(100001, "用户名重复"),
    DATA_NOT_FOUND(100002, "数据不存在"),
    USER_NOT_FOUND(100003, "用户不存在");
    private String message;
    private int code;

    public String getMessage() {
        return message;
    }

    public int getCode() {
        return code;
    }

    ExceptionEnum(int code, String message) {
        this.message = message;
        this.code = code;
    }
}

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值