源码地址:https://github.com/Jieszs/springboot-2.x-learning
思路:
拦截后端的异常和控制器的返回结果,根据前端的请求头,选择该国语言的文本内容,实现国际化
步骤:
1.引入依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
2.文件结构:
3.拦截全局异常,自定义异常
/**
* @author zj
* @date 2019/11/4
* 全局异常处理
*/
@ControllerAdvice
public class GlobalExceptionHandler {
@Resource
private InternationalUtil internationalUtil;
/**
* 400,用来处理bean字段异常
*
* @param ex
* @return
*/
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity resolveConstraintViolationException(ConstraintViolationException ex) {
Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
if (!CollectionUtils.isEmpty(constraintViolations)) {
StringBuilder msgBuilder = new StringBuilder();
for (ConstraintViolation constraintViolation : constraintViolations) {
msgBuilder.append(constraintViolation.getMessage()).append(",");
}
String errorMessage = msgBuilder.toString();
if (errorMessage.length() > 1) {
errorMessage = errorMessage.substring(0, errorMessage.length() - 1);
}
return buildResponse(errorMessage, HttpStatus.BAD_REQUEST);
}
return buildResponse(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
/**
* 400,用来处理方法参数异常
*
* @param ex
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity resolveMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
List<ObjectError> objectErrors = ex.getBindingResult().getAllErrors();
if (!CollectionUtils.isEmpty(objectErrors)) {
StringBuilder msgBuilder = new StringBuilder();
for (ObjectError objectError : objectErrors) {
msgBuilder.append(objectError.getDefaultMessage()).append(",");
}
String errorMessage = msgBuilder.toString();
if (errorMessage.length() > 1) {
errorMessage = errorMessage.substring(0, errorMessage.length() - 1);
}
return buildResponse(errorMessage, HttpStatus.BAD_REQUEST);
}
return buildResponse(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
/**
* 400
*
* @param e
* @return
*/
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity handleIllegalArgumentException(Exception e) {
return buildResponse(e.getMessage(), HttpStatus.BAD_REQUEST);
}
/**
* 403
*
* @param e
* @return
*/
@ExceptionHandler(BusinessException.class)
public ResponseEntity handleBusinessException(Exception e) {
return buildResponse(e.getMessage(), HttpStatus.FORBIDDEN);
}
/**
* 构建响应实体
*
* @param msg
* @param status
* @return
*/
private ResponseEntity buildResponse(String msg, HttpStatus status) {
return buildResponse(msg, status, null);
}
/**
* 构建响应实体
*
* @param msg
* @param status
* @return
*/
private ResponseEntity buildResponse(String msg, HttpStatus status, String result) {
JSONObject body = new JSONObject();
body.put("status", status.value());
body.put("error", status);
body.put("message", internationalUtil.getInterNationalMsg(msg));
return new ResponseEntity(body, status);
}
}
/**
* @author zj
* @date 2019/11/4
* 业务异常
*/
public class BusinessException extends Exception {
public BusinessException(String message) {
super(message);
}
}
4.拦截控制器
/**
* @author zj
* @date 2019/11/4
* 全局控制器处理
*/
@ControllerAdvice
public class ResponseBodyInterceptor implements ResponseBodyAdvice {
@Resource
private InternationalUtil internationalUtil;
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
//控制器结果为字符串才进行处理
if (body instanceof String) {
return internationalUtil.getInterNationalMsg((String)body);
}
return body;
}
}
5.国际化工具类
/**
* 国际化工具类
* @author zj
* @date 2019/11/4
*/
@Component
public class InternationalUtil {
@Resource
private HttpServletRequest request;
private static final String ACCEPT_LANGUAGE ="Accept-Language";
private static final String FILTER_PREFIX ="#";
private static final String LANGUAGE_SEPARATOR ="-";
/**
* 获取国际化消息
* @param key 国际化的key
* @param args 被替换占位符的值
* 1.带#号的不是key的字符串,直接过滤#号
* 2.是key的字符串,有占位符的替换
* 3.不是key的字符串,返回字符串本身
*
*/
public String getInterNationalMsg(String key, Object... args) {
//带#号的不是key的字符串,直接过滤#号
if (key.startsWith(FILTER_PREFIX)) {
return key.substring(1);
}
ResourceBundle rb;
String requestHeader = request.getHeader(ACCEPT_LANGUAGE);
//是key的字符串,有占位符的替换
try {
//如en-US,zh-CN
if (!StringUtils.isEmpty(requestHeader) && requestHeader.contains(LANGUAGE_SEPARATOR)) {
rb = ResourceBundle.getBundle("msg", new Locale(requestHeader.split(LANGUAGE_SEPARATOR)[0], requestHeader.split(LANGUAGE_SEPARATOR)[1]));
} else {
rb = ResourceBundle.getBundle("msg");
}
return MessageFormat.format(rb.getString(key), args);
} catch (Exception e) {
//不是key的字符串,返回字符串本身
return key;
}
}
}
6.Resource Bundle
接着
编辑
编辑
7.测试
控制器
/**
* @author zj
* @date 2019/11/4
*/
@RestController
public class HelloController {
@Resource
private InternationalUtil internationalUtil;
/**
* 测试控制器结果国际化
*
* @return
*/
@RequestMapping("/hello")
public String hello() {
return "HELLO_WORLD";
}
/**
* 测试异常国际化
*
* @return
*/
@RequestMapping("/exception")
public void exception() throws BusinessException {
throw new BusinessException("BUSINESS_EXCEPTION");
}
/**
* 测试不国际化
*
* @return
*/
@RequestMapping("/helloFilter")
public String helloFilter() {
return "#hello world!";
}
/**
* 测试占位符
*
* @return
*/
@RequestMapping("/helloUser")
public String helloUser(String name) {
return internationalUtil.getInterNationalMsg("HELLO_USER",name);
}
}
Resource bundle
BUSINESS_EXCEPTION=Business exception!
HELLO_USER=hello,{0}!
HELLO_WORLD=hello world!
BUSINESS_EXCEPTION=\u4e1a\u52a1\u5f02\u5e38\uff01
HELLO_USER=\u7528\u6237\u007b\u0030\u007d\u002c\u4f60\u597d\uff01
HELLO_WORLD=\u4f60\u597d\uff0c\u4e16\u754c\uff01
测试结果: