SpringBoot项目中实现返回结果和枚举类的国际化

前言

什么是国际化呢?国际惯例,来时来一段官方介绍:

  1. 国际化(internationalization)是设计和制造容易适应不同区域要求的产品的一种方式。
  2. 它要求从产品中抽离所有地域语言,国家/地区和文化相关的元素。
  3. 换言之,应用程序的功能和代码设计考虑在不同地区运行的需要,其代码简化了不同本地版本的生产。
  4. 开发这样的程序的过程,就称为国际化。

在我们实际开发中,一个web应用可能要在多个地区使用,面对不同地区的不同语言,为了适应不同的用户,我们可以尝试在前端页面实现多语言的支持,那么同样对于后端返回的一些提示信息,异常信息等,我们后端也可以根据不同的语言环境来进行国际化处理,返回相应的信息。

开发工具

IDEA、Maven、SpringBoot2.0.5、Jdk1.8、google浏览器

SpringBoot中的国际化

原理:

想要使应用支持国际化,首先需要知道用户的语言环境,即用户想要看到的语言,我们设想在用户每次请求时告诉服务器自己的语言环境,服务器收到请求后,根据不同的语言环境返回不同的信息来实现国际化。在spring应用中,用户的语言环境是通过区域解析器来识别的,而区域解析器有分为好几种(后面详细说),在我们不做配置修改时,spring使用
AcceptHeaderLocaleResolver作为默认的区域解析器,它是根据HTTP请求 Header中的Accept-language的值来解析,当然区域解析器我们也可以自定义配置。

springboot默认就支持国际化。我们只需要只需要作相应的配置即可。

1.首先你需要一个springboot项目。IDEA中分分钟创建好一个项目。

2.在resources下定义国际化配置文件,注意名称必须以messages开始。(在springboot中,当我们不修改配置时默认去解析名称以message开始的properties文件)

messages.properties (默认环境,无法确定语言环境时,解析该文件中的相应信息)

messages_zh_CN.properties(中文语言环境时,解析该文件中的相应信息)

messages_en_US.properties(英文语言环境时,解析该文件中的相应信息)

在三个配置文件中分别以Key = Value形式存储如下三条信息,如下:

welcome = 这是一个支持国际化的项目。

welcome = 这是一个支持国际化的项目。

  • welcome = This is a project supporting internationalization.

3.创建thymeleaf页面

加入thymeleaf依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  4. </dependency>

在resources/templates目录下创建hello.html页面:

尝试在不同的语言环境下,通过#{welcome}获取信息

  1. <!DOCTYPE html>
  2. <html xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <meta charset="UTF-8"/>
  5. <title>demo</title>
  6. </head>
  7. <body>
  8. <p><label th:text="#{welcome}"></label></p>
  9. </body>
  10. </html>

4.创建访问页面的controller

注意这里controller的注解时@Controller

  1. import org.springframework.stereotype.Controller;
  2. import org.springframework.web.bind.annotation.RequestMapping;
  3. /**
  4. * 描述:
  5. *
  6. * @author zhengql
  7. * @date 2018/9/25 19:28
  8. */
  9. @Controller
  10. public class BaseController {
  11. @RequestMapping("/hi")
  12. public String hello() {
  13. return "/hello";
  14. }
  15. }

测试国际化效果

这里使用google浏览器进行测试,测试之前需要安装插件Language Switcher

Language Switcher : 可以改变当前请求的语言环境(根据自己的选择)

启动我们的springboot项目,google浏览器访问 http://127.0.0.1:8080/hi ,可以看到如下页面:

通过Language Switcher切换语言环境为English - United States,重新访问 http://127.0.0.1:8080/hi ,可以看到如下页面:

ok,大功告成,到此一个简单的国际化项目就完成了。

扩展国际化

通过上面的小栗子,我们可以看到一个简单的国际化使用,但是在开发中中还需要我们进行一定的配置,来满足我们不同情况下的使用。

在返回结果中获取国际化信息

很多时候,后端接收到一个请求后,需要返回一个提示信息,而此时我们可以使这个返回信息支持国际化

这里就用到了
org.springframework.context.MessageSource接口,MessageSource提供了三个方法

@Nullable//参数字段可为空

  1. String getMessage(String var1, @Nullable Object[] var2, @Nullable String var3, Locale var4);
  2. String getMessage(String var1, @Nullable Object[] var2, Locale var3) throws NoSuchMessageException;
  3. String getMessage(MessageSourceResolvable var1, Locale var2) throws NoSuchMessageException;

String getMessage(String var1, @Nullable Object[] var2, @Nullable String var3, Locale var4):用来从MessageSource获取消息的基本方法。如果在指定的locale中没有找到消息,则使用默认的消息。var2中的参数将使用标准类库中的MessageFormat来作消息中替换值。

String getMessage(String code, Object[] args, Locale loc):本质上和上一个方法相同,其区别在:没有指定默认值,如果没找到消息,会抛出一个NoSuchMessageException异常。

String getMessage(MessageSourceResolvable resolvable, Locale locale):上面方法中所使用的属性都封装到一个MessageSourceResolvable实现中,而本方法可以指定MessageSourceResolvable实现。

下面我们实践一下:

1.创建一个以json返回格式的controller,注入MessageSource,注意controller的注解为@RestController

在这里首先我们需要获取到当前请求的Locale,有两种方法:

  1. Locale locale = LocaleContextHolder.getLocale();
  2. Locale locale = RequestContextUtils.getLocale(request);

两种方式根据情况选择使用,下面是controller代码

  1. package com.example.i18n.controller;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.context.MessageSource;
  4. import org.springframework.context.i18n.LocaleContextHolder;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. /**
  8. * 描述:
  9. *
  10. * @author zhengql
  11. * @date 2018/9/21 10:54
  12. */
  13. @RestController
  14. public class JsonController {
  15. @Autowired
  16. private MessageSource messageSource;
  17. @RequestMapping("/ha")
  18. public String ha() {
  19. return messageSource.getMessage("welcome", null, LocaleContextHolder.getLocale());
  20. }
  21. }

2.启动项目访问 http://127.0.0.1:8080/ha ,可以看到相应语言环境的返回信息

通过Language Switcher切换语言环境为English - United States,重新访问http://127.0.0.1:8080/ha,可以看到如下页面:

支持占位符国际化信息返回

我们经常会遇到这样一个场景,登录账号需要验证码,填写完手机号获取验证码后会收到一条类似于尊敬的用户13099999999您好,您的验证码是6666,这种信息,其实就是一个模板,通过改变参数,重复使用。我们通过国际化资源文件中的占位符,配合MessageSource提供的api也可以实现。

资源文件中加入如下属性:

messages.properties,messages_zh_CN.properties

  1. hello=你好:{0} , 你的验证码为 :{1}

messages_en_US.properties

  1. hello=Hello: {0}, your verification code is: {1}

我们在JsonController中,创建一个测试接口

  1. @RequestMapping("/haha")
  2. public String haha() {
  3. return messageSource.getMessage("hello", new Object[]{"zhangsan","123456"}, LocaleContextHolder.getLocale());
  4. }

启动项目直接,访问
http://127.0.0.1:8080/haha 可以看到相应语言环境的返回信息

通过Language Switcher切换语言环境为English - United States,重新访问
http://127.0.0.1:8080/haha ,可以看到如下页面:

支持国际化的枚举类

既然返回信息可以实现国际化,那我们的枚举类同样也可以实现国际化咯

创建一个枚举类EnumSuccessOrError.java

  1. /**
  2. * 描述:枚举类举例
  3. *
  4. * @author zhengql
  5. * @date 2018/9/26 20:52
  6. */
  7. public enum EnumSuccessOrError {
  8. SUCCESS(0, "操作成功"),
  9. ERROR(1, "操作失败");
  10. /**
  11. * 返回状态码
  12. */
  13. private int statusCode;
  14. /**
  15. * 返回状态信息
  16. */
  17. private String statusMsg;
  18. EnumSuccessOrError(int statusCode, String statusMsg) {
  19. this.statusCode = statusCode;
  20. this.statusMsg = statusMsg;
  21. }
  22. /**
  23. * @return the statusCode
  24. */
  25. public int getStatusCode() {
  26. return statusCode;
  27. }
  28. /**
  29. * @return the statusMsg
  30. */
  31. public String getStatusMsg() {
  32. return statusMsg;
  33. }
  34. }

如上,刚刚创建的枚举类是不支持国际化的,我们呢需要改造他,当调用getStatusMsg方法时根据语言环境返回相应的国际化字符串。可以从如下两个点着手:

getStatusMsg方法改造

资源文件中添加不同语言环境对应的返回值

先在三个资源文件中加入不同环境的返回值:

messages.properties,messages_zh_CN.properties

  1. SUCCESS = 操作成功
  2. ERROR = 操作失败

messages_en_US.properties

  1. SUCCESS=success
  2. ERROR=error

改造后的枚举如下:

  1. public enum EnumSuccessOrError {
  2. SUCCESS(0, "SUCCESS"),
  3. ERROR(1, "ERROR");
  4. /**
  5. * 返回状态码
  6. */
  7. private int statusCode;
  8. /**
  9. * 返回状态信息
  10. */
  11. private String statusMsg;
  12. EnumSuccessOrError(int statusCode, String statusMsg) {
  13. this.statusCode = statusCode;
  14. this.statusMsg = statusMsg;
  15. }
  16. private MessageSource messageSource;
  17. public EnumSuccessOrError setMessageSource(MessageSource messageSource) {
  18. this.messageSource = messageSource;
  19. return this;
  20. }
  21. //通过静态内部类的方式注入bean,并赋值到枚举中
  22. @Component
  23. public static class ReportTypeServiceInjector {
  24. @Autowired
  25. private MessageSource messageSource;
  26. @PostConstruct
  27. public void postConstruct() {
  28. for (EnumSuccessOrError rt : EnumSet.allOf(EnumSuccessOrError.class))
  29. rt.setMessageSource(messageSource);
  30. }
  31. }
  32. /**
  33. * @return the statusCode
  34. */
  35. public int getStatusCode() {
  36. return statusCode;
  37. }
  38. /**
  39. * @return the statusMsg,根据语言环境返回国际化字符串
  40. */
  41. public String getStatusMsg() {
  42. return messageSource.getMessage(statusMsg,null,statusMsg, LocaleContextHolder.getLocale());
  43. }

此时我们在JsonController中,再创建一个测试接口

  1. @RequestMapping("/enumDemo")
  2. public String enumDemo() {
  3. return EnumSuccessOrError.SUCCESS.getStatusMsg();
  4. }

启动项目直接,访问
http://127.0.0.1:8080/enumDemo 可以看到相应语言环境的返回信息。

<think>嗯,用户问的是在Spring Boot项目中如何设计统一响应体类。这个问题应该是关于后端开发的,特别是RESTful API的设计。我需要先回想一下常见的做法。 首先,统一响应体的目的是为了让前端或其他客户端接收到结构一致的响应,方便处理。通常包含状态码、消息、数据这几个部分。可能需要考虑成功失败的情况,所以状态码应该有成功错误的区分。 那怎么开始设计这个类呢?可能需要一个泛型类,因为返回的数据类型可能不同。比如,有的接口返回用户信息,有的返回列表,泛型可以灵活处理。然后,状态码可以用枚举来定义,这样更规范。比如,定义一个ResultCode枚举,包含codemessage字段。 接下来,响应体类可能包含code、message、data这三个字段。然后提供一些静态方法方便创建成功或失败的响应对象。比如success(data)返回code 200,message“成功”,以及data。而fail的话,可能传入错误码错误信息,或者直接使用预定义的枚举。 另外,可能还需要考虑分页的情况,如果有分页数据,可以在data里包含列表总条数等信息。这时候可以再设计一个分页响应类,包含itemstotal,然后作为data的一部分。 还有,是否要处理异常情况?比如全局异常处理,捕获异常后返回统一的错误格式。这时候响应体类可能需要支持传入异常信息,并转换为对应的错误码消息。 可能用户还希望知道如何在实际Controller中使用这个响应体。比如,原本返回User对象的方法,现在应该返回ResponseResult<User>,或者通过注解自动包装。不过Spring Boot中更常见的做法是使用@RestControllerAdvice配合ResponseBodyAdvice接口来自动包装返回值,这样Controller可以直接返回数据对象,由全局处理类封装成统一响应体。 但用户的问题可能更偏向于响应体类的结构设计,而不是自动包装的实现。所以重点还是放在类的定义上,包括字段、构造方法、静态工厂方法等。 需要考虑是否提供链式调用的方法,比如builder模式,但静态方法可能更简洁。比如ResponseResult.success().code(200).message("成功").data(user).build(),但使用静态方法如ResponseResult.ok(data)可能更方便。 另外,是否需要序列化的问题,比如字段命名用下划线还是驼峰,可能用@JsonInclude来忽略null值,避免返回空字段。 总结一下步骤:定义枚举类管理状态码,设计泛型响应体类包含code、message、data,提供静态方法创建成功失败对象,处理分页情况,可能配合全局异常处理返回错误信息。 可能用户还不太清楚如何具体编码,所以需要给出示例代码,包括类定义、枚举、以及使用示例。比如: public class ResponseResult<T> { private int code; private String message; private T data; // 构造方法、getter/setter public static <T> ResponseResult<T> success(T data) { return new ResponseResult<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); } // 其他静态方法 } 然后枚举类定义SUCCESSERROR等状态。同时,可以提到分页的情况,比如PageResult类包含数据总数。 最后,在Controller中的使用示例,返回ResponseResult.success(userService.getUserById(id))。 可能还需要提到Swagger等文档工具的整合,确保生成的文档显示正确的响应模型。 另外,是否需要考虑国际化?可能消息字段需要根据语言环境变化,但统一响应体可能暂时不需要处理,除非用户特别要求。 总之,关键点包括:使用泛型、状态码枚举、静态工厂方法、分页处理,以及可能的全局异常处理配合。</think>在Spring Boot项目中设计统一响应体类时,可通过以下步骤实现标准化响应结构: --- ### 一、基础响应体设计(核心三要素) ```java public class R<T> { private Integer code; // 状态码 private String msg; // 描述信息 private T data; // 业务数据(泛型) // 成功响应(无数据) public static <T> R<T> ok() { return new R<>(200, "操作成功", null); } // 成功响应(带数据) public static <T> R<T> ok(T data) { return new R<>(200, "操作成功", data); } // 失败响应 public static <T> R<T> fail(int code, String msg) { return new R<>(code, msg, null); } // 构造器(建议保留全参构造) public R(int code, String msg, T data) { this.code = code; this.msg = msg; this.data = data; } // getters/setters省略(需补充完整) } ``` --- ### 二、状态码枚举管理(规范错误码) ```java public enum ResultCode { SUCCESS(200, "成功"), BAD_REQUEST(400, "参数错误"), UNAUTHORIZED(401, "未授权"), FORBIDDEN(403, "禁止访问"), NOT_FOUND(404, "资源不存在"), INTERNAL_ERROR(500, "服务器内部错误"); private final int code; private final String message; // 构造器 + getters } ``` --- ### 三、分页数据扩展设计 ```java public class PageResult<T> { private List<T> list; // 当前页数据 private Long total; // 总记录数 private Integer pageNum; // 当前页码 private Integer pageSize; // 每页数量 // 构造器 + getters/setters } // 使用示例 R<PageResult<User>> pageResult = R.ok(new PageResult<>(userList, total)); ``` --- ### 四、全局异常处理(统一错误响应) ```java @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public R<Void> handleException(Exception e) { return R.fail(ResultCode.INTERNAL_ERROR.getCode(), e.getMessage()); } @ExceptionHandler(MethodArgumentNotValidException.class) public R<Void> handleValidationException(MethodArgumentNotValidException ex) { String errorMsg = ex.getBindingResult() .getFieldErrors() .stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining(", ")); return R.fail(ResultCode.BAD_REQUEST.getCode(), errorMsg); } } ``` --- ### 五、实际使用示例 ```java @GetMapping("/users/{id}") public R<User> getUser(@PathVariable Long id) { User user = userService.getById(id); return user != null ? R.ok(user) : R.fail(ResultCode.NOT_FOUND); } @PostMapping("/users") public R<Void> createUser(@Valid @RequestBody UserDTO dto) { userService.create(dto); return R.ok(); } ``` --- ### 六、Swagger集成展示 ```java @Schema(description = "统一响应实体") public class R<T> { @Schema(description = "状态码", example = "200") private Integer code; @Schema(description = "提示信息", example = "操作成功") private String msg; @Schema(description = "业务数据") private T data; } ``` --- ### 设计要点总结 1. **标准化结构**:固定`code-msg-data`三要素,前端无需处理不同格式 2. **类型安全**:使用泛型保持数据类型一致性 3. **错误码统一管理**:通过枚举避免硬编码 4. **异常处理**:全局捕获异常生成标准错误响应 5. **扩展性**:支持分页等复杂数据结构 6. **文档友好**:结合Swagger生成清晰API文档 建议通过`ResponseBodyAdvice`实现自动包装响应,避免每个Controller方法手动返回`R<T>`对象,保持代码简洁性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值