SpringBoot接口国际化异常信息的完整实现方案

 更新时间:2025年12月15日 08:57:16   作者:哈哈哈笑什么  
要实现Java后端接口根据请求头的语言字段返回对应语言的异常信息,核心思路是国际化配置 + 全局异常处理 + 请求头语言解析,以下是基于Spring Boot的完整实现方案,需要的朋友可以参考下

一、整体方案设计

  1. 语言标识约定:请求头中自定义lang字段(或复用Accept-Language),值如zh-CN(中文)、en-US(英文),默认值zh-CN
  2. 国际化资源文件:存放不同语言的错误信息模板。
  3. 自定义异常类:携带错误码和参数,便于匹配国际化信息。
  4. 语言解析工具:从请求头提取语言标识,转换为Locale对象。
  5. 全局异常处理器:捕获异常后,根据语言解析结果加载对应语言的错误信息并返回。
  6. MessageSource配置:加载国际化资源文件,支持参数替换。

二、具体实现步骤

1. 配置国际化资源文件

src/main/resources下创建i18n目录,存放多语言配置文件:

messages_zh_CN.properties(中文)

# 业务异常
error.user.not.found=用户不存在,用户ID:{0}
error.param.invalid=参数无效,参数名:{0}
# 系统异常
error.system.error=系统内部错误,请稍后重试

messages_en_US.properties(英文)

# 业务异常
error.user.not.found=User not found, User ID: {0}
error.param.invalid=Invalid parameter, Parameter name: {0}
# 系统异常
error.system.error=System internal error, please try again later

2. 自定义业务异常类

创建BusinessException,用于抛出业务相关异常,携带错误码和参数:

package com.example.demo.exception;

import lombok.Getter;

/**
 * 自定义业务异常
 */
@Getter
public class BusinessException extends RuntimeException {
    // 错误码(对应国际化配置文件的key)
    private final String errorCode;
    // 错误信息参数(用于替换国际化模板中的占位符)
    private final Object[] args;

    public BusinessException(String errorCode) {
        this(errorCode, null);
    }

    public BusinessException(String errorCode, Object... args) {
        super(errorCode);
        this.errorCode = errorCode;
        this.args = args;
    }
}

3. 语言解析工具类

创建LocaleUtils,从Http请求头解析语言标识,转换为Locale

package com.example.demo.utils;

import jakarta.servlet.http.HttpServletRequest;
import java.util.Locale;

/**
 * 语言解析工具类
 */
public class LocaleUtils {
    // 请求头中语言字段名(自定义,也可复用Accept-Language)
    private static final String LANG_HEADER = "lang";
    // 默认语言
    private static final Locale DEFAULT_LOCALE = Locale.SIMPLIFIED_CHINESE;

    /**
     * 从请求头解析Locale
     */
    public static Locale getLocaleFromRequest(HttpServletRequest request) {
        if (request == null) {
            return DEFAULT_LOCALE;
        }
        // 获取请求头中的lang值
        String lang = request.getHeader(LANG_HEADER);
        if (lang == null || lang.trim().isEmpty()) {
            return DEFAULT_LOCALE;
        }
        // 解析lang值(支持zh-CN、en-US、zh、en等格式)
        String[] langParts = lang.split("-");
        return switch (langParts.length) {
            case 1 -> new Locale(langParts[0]); // 如zh -> Locale("zh")
            case 2 -> new Locale(langParts[0], langParts[1]); // 如zh-CN -> Locale("zh", "CN")
            default -> DEFAULT_LOCALE;
        };
    }
}

4. 配置MessageSource(加载国际化资源)

在Spring Boot配置类中注册MessageSource Bean,加载国际化资源文件:

package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;

import java.nio.charset.StandardCharsets;
import java.util.Locale;

/**
 * 国际化配置
 */
@Configuration
public class I18nConfig {

    /**
     * 配置MessageSource,加载国际化资源文件
     */
    @Bean
    public ResourceBundleMessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        // 指定资源文件基础名(i18n目录下的messages)
        messageSource.setBasename("i18n/messages");
        // 设置编码,避免中文乱码
        messageSource.setDefaultEncoding(StandardCharsets.UTF_8.name());
        // 默认语言
        messageSource.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
        // 缓存时间(秒),开发时设为0,生产可设为3600
        messageSource.setCacheSeconds(0);
        return messageSource;
    }

    /**
     * 配置LocaleResolver(可选,复用Accept-Language时生效)
     */
    @Bean
    public LocaleResolver localeResolver() {
        AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver();
        resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
        return resolver;
    }
}

5. 全局异常处理器

创建GlobalExceptionHandler,捕获异常并返回对应语言的错误信息:

package com.example.demo.exception;

import com.example.demo.utils.LocaleUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.context.MessageSource;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.Locale;

/**
 * 全局异常处理器
 */
@RestControllerAdvice
@AllArgsConstructor
public class GlobalExceptionHandler {

    // 注入国际化消息源
    private final MessageSource messageSource;

    /**
     * 处理业务异常
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<?> handleBusinessException(BusinessException e, HttpServletRequest request) {
        // 解析请求头的语言
        Locale locale = LocaleUtils.getLocaleFromRequest(request);
        // 从国际化配置中获取对应语言的错误信息
        String errorMessage = messageSource.getMessage(
                e.getErrorCode(), // 错误码(对应配置文件的key)
                e.getArgs(),      // 占位符参数
                e.getErrorCode(), // 默认值(配置文件无该key时使用)
                locale            // 语言
        );
        return Result.fail(HttpStatus.BAD_REQUEST.value(), errorMessage);
    }

    /**
     * 处理系统异常
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<?> handleSystemException(Exception e, HttpServletRequest request) {
        Locale locale = LocaleUtils.getLocaleFromRequest(request);
        String errorMessage = messageSource.getMessage(
                "error.system.error",
                null,
                "System internal error",
                locale
        );
        // 打印系统异常栈(生产环境可接入日志框架)
        e.printStackTrace();
        return Result.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), errorMessage);
    }

    /**
     * 统一返回结果封装
     */
    @Data
    @AllArgsConstructor
    public static class Result<T> {
        private int code;       // 状态码
        private String message; // 错误信息
        private T data;         // 数据(异常时为null)

        public static <T> Result<T> fail(int code, String message) {
            return new Result<>(code, message, null);
        }
    }
}

6. 接口示例(测试异常返回)

创建UserController,模拟查询用户接口,不存在时抛业务异常:

package com.example.demo.controller;

import com.example.demo.exception.BusinessException;
import com.example.demo.exception.GlobalExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 测试接口
 */
@RestController
@RequestMapping("/users")
public class UserController {

    /**
     * 根据用户ID查询用户
     */
    @GetMapping("/{userId}")
    public GlobalExceptionHandler.Result<?> getUser(@PathVariable Long userId) {
        // 模拟用户不存在的场景
        if (userId <= 0) {
            // 抛业务异常,携带错误码和参数(用户ID)
            throw new BusinessException("error.user.not.found", userId);
        }
        return new GlobalExceptionHandler.Result<>(200, "success", "用户信息:" + userId);
    }
}

三、测试验证

使用Postman/Curl调用接口,通过请求头lang指定语言:

1. 测试中文返回(lang=zh-CN)

请求:

GET http://localhost:8080/users/-1
Header: lang=zh-CN

响应:

{
  "code": 400,
  "message": "用户不存在,用户ID:-1",
  "data": null
}

2. 测试英文返回(lang=en-US)

请求:

GET http://localhost:8080/users/-1
Header: lang=en-US

响应:

{
  "code": 400,
  "message": "User not found, User ID: -1",
  "data": null
}

3. 测试默认语言(不传递lang)

请求:

GET http://localhost:8080/users/-1

响应:

{
  "code": 400,
  "message": "用户不存在,用户ID:-1",
  "data": null
}

四、扩展说明

复用Accept-Language:若想复用HTTP标准头Accept-Language,只需修改LocaleUtils中的LANG_HEADERAccept-Language,并适配解析逻辑(Accept-Language格式如zh-CN,zh;q=0.9,en;q=0.8)。

更多语言支持:新增messages_ja_JP.properties(日语)等配置文件,即可支持更多语言,无需修改代码。

错误码规范:建议将错误码枚举化(如ErrorCode.USER_NOT_FOUND),避免硬编码。

生产环境优化

  1. 异常栈信息不要返回给前端,仅打印到日志;
  2. MessageSourcecacheSeconds设为3600,提升性能;
  3. 接入日志框架(如Logback/Log4j2)记录异常详情。

五、核心依赖(pom.xml)

确保Spring Boot基础依赖已引入:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

该方案实现了异常信息的国际化,符合RESTful接口设计规范,且易于扩展和维护。

以上就是SpringBoot接口国际化异常信息的完整实现方案的详细内容,更多关于SpringBoot接口国际化异常信息的资料请关注脚本之家其它相关文章!

相关文章

  • 三级联动省市ajax的代码

    三级联动省市ajax的代码

    这篇文章主要为大家详细介绍了ajax实现省市三级联动效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能给你带来帮助
    2021-07-07
  • SpringBoot启动后启动内嵌浏览器的方法

    SpringBoot启动后启动内嵌浏览器的方法

    这篇文章主要介绍了SpringBoot启动后启动内嵌浏览器的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • Springboot项目Aop与拦截器与过滤器横向对比

    Springboot项目Aop与拦截器与过滤器横向对比

    前三篇文章已经介绍过Springboot项目如何实现Aop,拦截器和过滤齐功能,这篇文章主要介绍三者的横向对比,本文有一定的参考价值,感兴趣的小伙伴可以参考阅读
    2023-03-03
  • Java 如何实现解压缩文件和文件夹

    Java 如何实现解压缩文件和文件夹

    这篇文章主要介绍了Java 如何实现解压缩文件和文件夹,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下
    2021-03-03
  • JavaWeb.servlet的基本使用方法详解

    JavaWeb.servlet的基本使用方法详解

    Servlet指在服务器端执行的一段Java代码,可以接收用户的请求和返回给用户响应结果,下面这篇文章主要给大家介绍了关于JavaWeb.servlet基本使用的相关资料,需要的朋友可以参考下
    2022-04-04
  • java求三个数的最大值的示例分享

    java求三个数的最大值的示例分享

    这篇文章主要介绍了java求三个数的最大值的示例分享,需要的朋友可以参考下
    2014-03-03
  • 详解java各种集合的线程安全

    详解java各种集合的线程安全

    这篇文章主要介绍了详解java各种集合的线程安全,小编觉得挺不错的,这里分享给大家,供需要的朋友参考。
    2017-10-10
  • Servlet第一个项目的发布(入门)

    Servlet第一个项目的发布(入门)

    这篇文章主要介绍了Servlet第一个项目的发布,下面是用servlet实现的一个简单的web项目,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2021-04-04
  • Java设计模式之Adapter适配器模式

    Java设计模式之Adapter适配器模式

    这篇文章主要为大家详细介绍了Java设计模式之Adapter适配器模式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • Spring Boot之内嵌tomcat版本升级操作示例

    Spring Boot之内嵌tomcat版本升级操作示例

    这篇文章主要为大家介绍了Spring Boot之内嵌tomcat版本升级操作示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06

最新评论