基于SpringCloud Gateway的API网关的网关服务接口实现i18n国际化

avatar
作者
筋斗云
阅读量:0

直达电梯

项目背景

项目架构是通过网关模块(基于SpringCloud gateway)路由到业务接口模块(基于SpringBoot),没有独立的鉴权服务,“登录接口”集成在网关模块本身。
使用的框架版本如下:

  • SpringBoot 2.7.5
  • SpringCloud gateway 2021.0.9

提出问题

  • SpringCloud Gateway是基于reactor模型的,按照SpringBoot那套以及所尝试网上以及AI的i18n国际化方案,都没有成功。
  • 重点是我的网关模块自身提供了接口给前端调用,这些网关模块自身的接口的国际化有问题。

解决问题

基本思路跟SpringBoot项目的i18n一样

  • 通过MessageSource加载messages国际化资源
  • 通过基于WebFilter的过滤器从请求头获取Content-Language识别locale
  • 需要国际化的地方通过MessageUtils.message()实现国际化

实现代码

项目结构

gateway 	src 		main 			java 			resources 				i18n 					messages.properties 					messages_en_US.properties 		test 

配置文件(application.yml)

仅摘取跟i18n相关部分

spring: 	# 资源信息    messages:       # 国际化资源文件路径       basename: i18n/messages 

代码

I18nGlobalFilter.java

import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; import reactor.core.publisher.Mono;  import java.util.Locale;  /**  * 国际化过滤器  * 根据浏览器请求头的里Content-Language实现国际化  *  * @Description  * @Author   * @Date 2024/4/29  */ @Component public class I18nGlobalFilter implements WebFilter {      @Override     public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {         Locale locale = Locale.getDefault(); // 默认Locale         HttpHeaders headers = exchange.getRequest().getHeaders();         String language = headers.getFirst("Content-Language");         if (language != null) {             locale = Locale.forLanguageTag(language);         }          // 将Locale存储在ServerWebExchange的属性中,供后续逻辑使用         exchange.getAttributes().put(Locale.class.getName(), locale);         // spring gateway手动处理,基于LocaleContextHolder         LocaleContextHolder.setLocale(locale);          return chain.filter(exchange);     } } 

MessageUtils.java

import com.siemens.tbds.gateway.util.SpringUtils; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder;  /**  * 获取i18n资源文件  *  * @Description  * @Author   * @Date 2024/4/29  */ public class MessageUtils {     /**      * 根据消息键和参数 获取消息 委托给spring messageSource      *      * @param code 消息键      * @param args 参数      * @return 获取国际化翻译值      */     public static String message(String code, Object... args) {         MessageSource messageSource = SpringUtils.getBean(MessageSource.class);         return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());     } } 

调用示例代码(仅调用片段)

return ApiResponse.error(MessageUtils.message("api.auth.username-password.error")); 

messages.properties示例

api.auth.username-password.error=用户名或密码错误! 

messages_en_US.properties示例

api.auth.username-password.error=username or password error. 

接口调用测试结果

  • 中文
    cn-ZH

  • 英文
    en-US

总结

  • 要点1:
    与常规SpringBoot的web项目不同之处在Filter,SpringBoot项目实现LocaleResolver接口即可(当然理论上跟SpringCloud gateway一样集成WebFilter也是可以的,因为WebFilter是spring-web里的接口);
  • 要点2
    网上文章以及AI的回答均是通过实现SpringCloud gateway的GlobalFilter接口,我最终都没有成功,根据资料显示GloabalFilter是进入gateway的routes路由的接口才会触发;而我这里是网关模块自己有接口给前端调用,这个接口的国际化无法通过实现GlobalFilter实现。

附SpringBoot常规web项目的实现代码

仅I18nConfig.java替换上面的I18nGlobalFilter.java即可,其余MessageUtils.java、i18n资源文件、application.yml中i18n配置均与上面保持一致即可。
I18nConfig.java

import cn.hutool.core.util.StrUtil; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.LocaleResolver;  import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Locale;  /**  * 国际化配置  *  * @author   */ @Configuration public class I18nConfig {      @Bean     public LocaleResolver localeResolver() {         return new I18nLocaleResolver();     }      /**      * 获取请求头国际化信息      */     static class I18nLocaleResolver implements LocaleResolver {          @Override         public Locale resolveLocale(HttpServletRequest httpServletRequest) {             String language = httpServletRequest.getHeader("content-language");             Locale locale = Locale.getDefault();             if (StrUtil.isNotBlank(language)) {                 String[] split = language.split("-");                 locale = new Locale(split[0], split[1]);             }             return locale;         }          @Override         public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {          }     } } 

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!