基于Spring实现自定义错误信息返回详解

2025-03-21 02:50

本文主要是介绍基于Spring实现自定义错误信息返回详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《基于Spring实现自定义错误信息返回详解》这篇文章主要为大家详细介绍了如何基于Spring实现自定义错误信息返回效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下...

背景

Spring 提供了 @RestConChina编程trollerAdvice 用来实现 HTTP 协议的全局异常处理。在异常信息的处理上通常只返回特定的 Response 对象,如下。

@Slf4j
@RestControllerAdvice
public class RestExceptionResolver {

	@ExceptionHandler(Exception.class)
	public ResponseEntity<?> processException(Exception ex) {
		BodyBuilder builder;
		Response response;
		ResponseStatus responseStatus =
			AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
		if (responseStatus != null) {
			builder = ResponseEntity.status(responseStatus.value());
			response = Response.buildFailure("500", responseStatus.reason());
		} else {
			builder = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
			response = Response.buildFailure("500", ex.getMessage());
		}
		this.process(ex, response);
		return www.chinasem.cnbuilder.body(response);
	}
}

作为基础框架,笔者就遇到项目A 要求返回 Response1 对象,项目B 要求返回 Response2 对象,这个时候,适配起来就很痛苦,例如下方的代码。

@Slf4j
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
@ToString(callSuper = true)
@Data
public class Response extends DTO {

	private static final long serialVersionUID = 1L;

	private boolean success;

	private String errCode; // 项目A 要求错误码是字符型

	private String errMessage; // 项目A 要求用这个名字

	private int code; // 项目B 要求错误码是整型

	private String message; // 项目B 要求用这个名字
}

另外,@RestControllerAdvice 只适用于 Web 异常捕获,我们还要考虑其他组件的情况,例如 Dubbo 捕获 RPC 异常、Sentinel 组件触发限流、Spring Security 安全框架抛出认证异常。

目标

不需要修改基础框架,允许业务方自行扩展异常返回对象。

实现

Response 提炼为 Builder 模式,改为 ResponseBuilder.builder() 构建返回对象。

@Slf4j
@RestControllerAdvice
public class RestExceptionHandler {

	@ExceptionHandler(Exception.class)
	public ResponseEntity<?> resolveException(Exception ex) {
		BodyBuilder builder;
		Object response;
		ResponseStatus status = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
		if (status != null) {
			builder = ResponseEntity.status(status.value());
			response = ResponseBuilder.builder().buildFailure("500", status.reason());
		} else {
			builder = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
			response = ResponseBuilder.builder().buildFailure("500", ex.getMessage());
		}
		this.postProcepythonss(ex);
		return builder.body(response);
	}
}

public interface ResponseBuilder<T> {

	static ResponseBuilder<?> builder() {
		// 尝试从业务项目获取自定义的 Spring Bean
		ResponseBuilder<?> builder = ApplicationContextHelper.ClPTOgetBean(ResponseBuilder.class);
		if (builder != null) {
			return builder;
		}
		// 如果业务项目没有自定义 Bean,返回默认的 Builder
		return DefalutResponseBuilder.getInstance();
	}

	T buildSuccess();

	<Body> T buildSuccess(Body data);

	T buildFailure(String errCode, String errMessage, Object... params);
}

public class DefalutResponseBuilder implements ResponseBuilder<Response> {

    private static final DefaultResponseBuilder INSTANCE = new DefaultResponseBuilder();

    private DefaultResponseBuilder() {}

    public static DefaultResponseBuilder getInstance() {
        return INSTANCE;
    }

	@Override
	puandroidblic Response buildSuccess() {
		Response response = new Response();
		response.setSuccess(true);
		return response;
	}

	@Override
	public <Body> Response buildSuccess(Body data) {
		SingleResponse<Body> response = new SingleResponse<>();
		response.setSuccess(true);
		response.setData(data);
		return response;
	}

	@Override
	public Response buildFailure(String errCode, String errMessage, Object... params) {
		Response response = new Response();
		response.setSuccess(false);
		response.setErrCode(errCode);
		response.setErrMessage(MessageFormatter.arrayFormat(message, placeholders).getMessage());
		return response;
	}
}

@Slf4j
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
@ToString(callSuper = true)
@Data
public class Response extends DTO {

	private static final long serialVersionUID = 1L;

	private boolean success;

	private String errCode;

	private String errMessage;
}

业务方觉得 Response 不能满足需求,重新定义了新对象,如下。

@Slf4j
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
@ToString(callSuper = true)
@Data
public class CustomResponse {

	private static final long serialVersionUID = 1L;

	private boolean success;

	private int code; // 要求错误码是整型

	private String message; // 前端要求用这个名字
}

创建 CustomResponseBuilder 包装 CustomResponse 对象,并标记 @Component 注解,放入 Spring Bean 管理。

@Component
public class CustomResponseBuilder implements ResponseBuilder<CustomResponse> {

	@Override
	public CustomResponse buildSuccess() {
		CustomResponse response = new CustomResponse();
		response.setSuccess(true);
		return response;
	}

	@Override
	public <Body> CustomResponse buildSuccess(Body data) {
		// 略
	}

	@Override
	public CustomResponse buildFailure(int code, String message, Object... params) {
		CustomResponse response = new CustomResponse();
		response.setSuccess(false);
		response.setCode(code);
		response.setMessage(MessageFormatter.arrayFormat(message, placeholders).getMessage());
		return response;
	}
}

上述已提到 ResponseBuilder.builder() 优先查找 Spring Bean,所以 CustomResponseBuilder 覆盖了框架内置的 DefaultResponseBuilder 类,全局异常捕获器返回结果时,就能返回业务方自定义的 CustomResponse 对象,这样,不需要改动框架,就能满足业务需求。

产出

根据这个思路,我们分别实现了 Web 异常、Dubbo 异常、Sentinel 限流、Security 认证等各种场景的异常处理机制,业务方只需要自行创建 ResponseBuilder 扩展自己的返回对象即可,不需要修改框架。

到此这篇关于基于Spring实现自定义错误信息返回详解的文章就介绍到这了,更多相关Spring自定义错误信息内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!

这篇关于基于Spring实现自定义错误信息返回详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1153863

相关文章

Spring Boot项目部署命令java -jar的各种参数及作用详解

《SpringBoot项目部署命令java-jar的各种参数及作用详解》:本文主要介绍SpringBoot项目部署命令java-jar的各种参数及作用的相关资料,包括设置内存大小、垃圾回收... 目录前言一、基础命令结构二、常见的 Java 命令参数1. 设置内存大小2. 配置垃圾回收器3. 配置线程栈大小

SpringBoot实现微信小程序支付功能

《SpringBoot实现微信小程序支付功能》小程序支付功能已成为众多应用的核心需求之一,本文主要介绍了SpringBoot实现微信小程序支付功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作... 目录一、引言二、准备工作(一)微信支付商户平台配置(二)Spring Boot项目搭建(三)配置文件

解决SpringBoot启动报错:Failed to load property source from location 'classpath:/application.yml'

《解决SpringBoot启动报错:Failedtoloadpropertysourcefromlocationclasspath:/application.yml问题》这篇文章主要介绍... 目录在启动SpringBoot项目时报如下错误原因可能是1.yml中语法错误2.yml文件格式是GBK总结在启动S

鸿蒙中@State的原理使用详解(HarmonyOS 5)

《鸿蒙中@State的原理使用详解(HarmonyOS5)》@State是HarmonyOSArkTS框架中用于管理组件状态的核心装饰器,其核心作用是实现数据驱动UI的响应式编程模式,本文给大家介绍... 目录一、@State在鸿蒙中是做什么的?二、@Spythontate的基本原理1. 依赖关系的收集2.

Spring中配置ContextLoaderListener方式

《Spring中配置ContextLoaderListener方式》:本文主要介绍Spring中配置ContextLoaderListener方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录Spring中配置ContextLoaderLishttp://www.chinasem.cntene

基于Python实现高效PPT转图片工具

《基于Python实现高效PPT转图片工具》在日常工作中,PPT是我们常用的演示工具,但有时候我们需要将PPT的内容提取为图片格式以便于展示或保存,所以本文将用Python实现PPT转PNG工具,希望... 目录1. 概述2. 功能使用2.1 安装依赖2.2 使用步骤2.3 代码实现2.4 GUI界面3.效

MySQL更新某个字段拼接固定字符串的实现

《MySQL更新某个字段拼接固定字符串的实现》在MySQL中,我们经常需要对数据库中的某个字段进行更新操作,本文就来介绍一下MySQL更新某个字段拼接固定字符串的实现,感兴趣的可以了解一下... 目录1. 查看字段当前值2. 更新字段拼接固定字符串3. 验证更新结果mysql更新某个字段拼接固定字符串 -

Python获取C++中返回的char*字段的两种思路

《Python获取C++中返回的char*字段的两种思路》有时候需要获取C++函数中返回来的不定长的char*字符串,本文小编为大家找到了两种解决问题的思路,感兴趣的小伙伴可以跟随小编一起学习一下... 有时候需要获取C++函数中返回来的不定长的char*字符串,目前我找到两种解决问题的思路,具体实现如下:

java实现延迟/超时/定时问题

《java实现延迟/超时/定时问题》:本文主要介绍java实现延迟/超时/定时问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java实现延迟/超时/定时java 每间隔5秒执行一次,一共执行5次然后结束scheduleAtFixedRate 和 schedu

Java Optional避免空指针异常的实现

《JavaOptional避免空指针异常的实现》空指针异常一直是困扰开发者的常见问题之一,本文主要介绍了JavaOptional避免空指针异常的实现,帮助开发者编写更健壮、可读性更高的代码,减少因... 目录一、Optional 概述二、Optional 的创建三、Optional 的常用方法四、Optio