java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot接口国际化异常信息

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

作者:哈哈哈笑什么

要实现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接口国际化异常信息的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文