【项目地址】 从零开始学习springmvc
如果觉得有用可以关注一下公众号:码字不易,求赞求关注
- 五、Spring国际化和全局异常处理
- 5.1 国际化介绍
- 5.1.1 ResourceBundle的介绍
- 5.1.2 ResourceBundle的使用
- 5.1.3 Spring国际化概述
- 5.1.3.1 Spring国际化资源文件约定
- 5.1.3.2 如何使用spring国际化
- 5.1.3.3 如何确定本地信息(获取Locale信息)
- 1. 前端如何传Locale信息
- 2. 后端如何获取Locale信息
- 5.2 全局异常处理
- 5.2.1 @ControllerAdvice注解
- 5.2.2 @ExceptionHandler注解
- 5.2.3 全局异常处理使用
- 5.2.4 响应体返回中文乱码
- 5.3 Spring国际化整合Hibernate参数校验
- 5.3.1 LocalValidatorFactoryBean配置validationMessageSource
- 5.3.2 三种常见参数校验异常
- 5.3.2.1 BindException
- 5.3.2.2 MethodArgumentNotValidException
- 5.3.2.3 ConstraintViolationException
- 5.3.3 国际化错误信息
- 5.3.4 全局异常处理
国际化的意思指对于同一个信息,可以识别不同的用户,从而展现出匹配用户当地语言信息。比如中文"提交",对于不懂中文的英国人你要使用“post”来表达一样。
对于页面来说,可以根据用户输入的Accept-Language请求头来识别用户语言环境,从而加载已经提前准备好的语言资源包,展现出适配用户语言的环境。
5.1.1 ResourceBundle的介绍ResourceBundle是java.util包下的工具类,主要用来解决国际化问题。当程序需要一个特定于语言环境的资源时(如 String),程序可以从适合当前用户语言环境的资源包(大多数情况下也就是.properties文件)中加载它。这样可以很大程度上独立于用户语言环境的程序代码,它将资源包中大部分(即便不是全部)特定于语言环境的信息隔离开来。
5.1.2 ResourceBundle的使用-
新建语言资源包,为了适配spring国际化资源包的要求如下:
- 新建资源包目录名为i18n目录
- 以messages开头命名资源文件,则此目录下以messages开头的properties文件会被认为是一个资源包,资源包的baseName为messages。格式为messages_语言(小写)_国家(大写),如messages_zh_CN.properties代表简体中文,messages_en_US.properties代表美式英语。
- messages.properties----存放不需要国际化的消息
# messages_zh_CN.properties response.404.code=404 response.404.message.0001=请求参数不合法。 # messages_en_US.properties response.404.code=404 response.404.message.0001=Request parameters are invalid.
-
使用ResourceBundle
package org.numb.common.util; import java.util.Locale; import java.util.ResourceBundle; import org.junit.Test; public class MessagesTest { @Test public void test_messagesConsistency() { ResourceBundle zhMessages = ResourceBundle.getBundle("i18n/messages", Locale.SIMPLIFIED_CHINESE); System.out.println(zhMessages.getString("response.404.message.0001")); ResourceBundle enMessages = ResourceBundle.getBundle("i18n/messages", Locale.US); System.out.println(enMessages.getString("response.404.message.0001")); } }
-
测试
一般需要两个条件才可以确定一个特定类型的本地化信息,它们分别是“语言类型”和“国家/地区的类型”。比如中国大陆地区的中文是zh_CN,美式英语是en_US,英式英语是en_GB,国际标准规范ISO-3166规定了常用的国家和地区编码,这里给出了一些常用的国家地区编码
i18n(来源于internationalization的首末字符i和n,18为中间的字符数)是“国际化”的简称,一般将资源文件放在resourcesi18n目录下,各资源文件以messages_开头
resources |----i18n | |----messages.properties // 不需要国际化的消息 | |----messages_zh_CN.properties // 中文消息 | |----messages_en_US.properties // 英文消息 | |----..... // 其他消息5.1.3.2 如何使用spring国际化
Spring国际化主要是依赖资源文件绑定器ResourceBundleMessageSource。
basename类似于【5.1.2 ResourceBundle的使用】中ResourceBundle的basename,要配到资源名下,这里i18n是文件夹,messages是资源包名。defaultEncoding指定编码方式
在使用之前为了方便引入日志框架slf4j+log4j2,可以参考我的log4j2专栏。
配置如下
ResourceBundleMessageSource使用
public final String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException
这里封装一个工具MessageProcessor,通过request的getLocale()方法获取Locale,ErrorCode封装所有的国际化key。
public class ErrorCode { public static final String INVALID_PARAMETERS_MESSAGE = "response.400.message.0001"; public static final String INTERNAL_ERROR_DEFAULT_MESSAGE = "response.500.message.0001"; }
@Component public class MessageProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(MessageProcessor.class); @Resource private ResourceBundleMessageSource messageSource; public String getMessage(HttpServletRequest request, String msgCode, Object... params) { if (request == null || msgCode == null) { throw new IllegalArgumentException("request or message code is null!"); } try { return messageSource.getMessage(msgCode, params, request.getLocale()); } catch (NoSuchMessageException exception) { LOGGER.error("can not find the message of the message code of {}", msgCode); return messageSource.getMessage(INTERNAL_ERROR_DEFAULT_MESSAGE, params, request.getLocale()); } } }
资源文件
response.400.message.0001=请求参数不合法 response.404.message.0001=资源不存在。 response.500.message.0001=系统错误,请稍后重试。 response.400.message.0001=Request parameters are invalid response.404.message.0001=Request resource can not be found. response.500.message.0001=System error, please try again later.
测试,UserController使用如下:
@RequestMapping(value = "/user/{user_id}", method = RequestMethod.DELETE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody @ResponseStatus(code = HttpStatus.OK) public String deleteUser(HttpServletRequest request, HttpServletResponse response, @PathVariable(value = "user_id") String userId) { String userName = userService.getUserName(userId); if (userName == null) { return messageProcessor.getMessage(request, INVALID_PARAMETERS_MESSAGE); } // delete user return "success"; }
ResourceBundleMessageSource也可以在国际化信息中传入参数,可以传入一个object[]数组,传入{0}, {1}…代表数组的每个索引的参数,如下所示
国际化资源包
response.400.message.0002=请求参数 {0} 不合法。 response.400.message.0002=Request parameters {0} are invalid.
UserController
@RequestMapping(value = "/user/{user_id}", method = RequestMethod.DELETE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody @ResponseStatus(code = HttpStatus.OK) public String deleteUser(HttpServletRequest request, HttpServletResponse response, @PathVariable(value = "user_id") String userId) { String userName = userService.getUserName(userId); if (userName == null) { return messageProcessor.getMessage(request, "response.400.message.0002", "userId"); } // delete user return "success"; }
测试:
5.1.3.3 如何确定本地信息(获取Locale信息)上面直接根据request.getLocale()获取的Locale信息,此方法会寻找Accept-Language请求头信息,如果没有则返回默认的Locale。上小节我们并未特意强调传入Accept-Language请求头,所以是获取的默认Locale。这样一个问题是服务部署在不同环境上,默认Locale会不同导致国际化失效。下面将明确指出本地化的方法。
1. 前端如何传Locale信息- 基于浏览器语言(常用方式):根据Request Headers中的Accept-Language来判断。
- 基于客户端传参:要求客户端第一次(或者每次)传递的自定义参数值来判断。如果在第一次传参中确定,那么locale信息要存入session或者cookie中,后面的请求语言方式则直接从两者中取,其有效时间与session和cookie设置的生命周期关联。这种方式一般用于需要覆盖请求头的Accept-Language。
- 基于默认配置:当获取语言类型时没有找到对应类型时,会使用默认的语言类型
-
请求头获取AcceptHeaderLocaleResolver:默认根据Accept-Language请求头判断Locale信息。
// 可以继承此类,重写resolveLocale方法,获取自定义的请求头 public class AcceptHeaderLocaleResolver implements LocaleResolver { // 获取Locale信息 @Override public Locale resolveLocale(HttpServletRequest request) { Locale defaultLocale = getDefaultLocale(); if (defaultLocale != null && request.getHeader("Accept-Language") == null) { return defaultLocale; } Locale requestLocale = request.getLocale(); List
supportedLocales = getSupportedLocales(); if (supportedLocales.isEmpty() || supportedLocales.contains(requestLocale)) { return requestLocale; } Locale supportedLocale = findSupportedLocale(request, supportedLocales); if (supportedLocale != null) { return supportedLocale; } return (defaultLocale != null ? defaultLocale : requestLocale); } // 设置Locale信息,默认不支持设置Locale信息 @Override public void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale) { throw new UnsupportedOperationException( "Cannot change HTTP accept header - use a different locale resolution strategy"); } } -
session获取SessionLocaleResolver:
public class SessionLocaleResolver extends AbstractLocaleContextResolver { // 根据session获取Locale信息,获取不到返回默认Locale @Override public LocaleContext resolveLocaleContext(final HttpServletRequest request) { return new TimeZoneAwareLocaleContext() { @Override public Locale getLocale() { Locale locale = (Locale) WebUtils.getSessionAttribute(request, localeAttributeName); if (locale == null) { locale = determineDefaultLocale(request); } return locale; } @Override public void setLocaleContext(HttpServletRequest request, @Nullable HttpServletResponse response,@Nullable LocaleContext localeContext) { Locale locale = null; TimeZone timeZone = null; if (localeContext != null) { locale = localeContext.getLocale(); if (localeContext instanceof TimeZoneAwareLocaleContext) { timeZone = ((TimeZoneAwareLocaleContext) localeContext).getTimeZone(); } } WebUtils.setSessionAttribute(request, this.localeAttributeName, locale); WebUtils.setSessionAttribute(request, this.timeZoneAttributeName, timeZone); } }
-
cookie获取cookieLocaleResolver:
public class cookieLocaleResolver extends cookieGenerator implements LocaleContextResolver { // 根据cookie的.LOCALE获取Locale信息 @Override public LocaleContext resolveLocaleContext(final HttpServletRequest request) { parseLocalecookieIfNecessary(request); return new TimeZoneAwareLocaleContext() { @Override @Nullable public Locale getLocale() { return (Locale) request.getAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME); } @Override @Nullable public TimeZone getTimeZone() { return (TimeZone) request.getAttribute(TIME_ZONE_REQUEST_ATTRIBUTE_NAME); } }; } @Override public void setLocaleContext(HttpServletRequest request, @Nullable HttpServletResponse response,@Nullable LocaleContext localeContext) { Assert.notNull(response, "HttpServletResponse is required for" + "cookieLocaleResolver"); Locale locale = null; TimeZone timeZone = null; if (localeContext != null) { locale = localeContext.getLocale(); if (localeContext instanceof TimeZoneAwareLocaleContext) { timeZone = ((TimeZoneAwareLocaleContext) localeContext).getTimeZone(); } addcookie(response, (locale != null ? toLocalevalue(locale) : "-") + (timeZone != null ? '/' + timeZone.getID() : "")); } else { removecookie(response); } request.setAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME, (locale != null ? locale : determineDefaultLocale(request))); request.setAttribute(TIME_ZONE_REQUEST_ATTRIBUTE_NAME, (timeZone != null ? timeZone : determineDefaultTimeZone(request))); } }
一般正常使用传入Accept-Language请求头即可
5.2 全局异常处理如果在Controller请求中触发了异常,未处理的话,默认会提示500,服务器内部错误。但是有些异常又不得不去处理,如果在业务代码抛异常处处理,那就会产生很多冗余代码。因为大多数异常触发的原因是相似的。幸好Spring也提供了通用机制去处理。
5.2.1 @ControllerAdvice注解@ControllerAdvice注解的类可以在多个Controller类(@Controller注解标注的类)之间共享由@ExceptionHandler、 @InitBinder、或者@ModelAttribute注解的代码,且该类必须由@Component注解。简而言之,一个被Spring托管的bean(@Component),如果加了@ControllerAdvice注解,则其内部的方法如果被@ExceptionHandler、 @InitBinder、或者@ModelAttribute注解,则所有的Controller只要满足特定条件,都可以走到这个类中的方法。
5.2.2 @ExceptionHandler注解@ExceptionHandler注解作用是Controller类中抛出异常,会在此处进行处理,可以用于类或者方法上。@ExceptionHandler注解的方法的参数可以是以下任意数量的参数:
- 异常参数:@ExceptionHandler中的value()指定得异常类;
- request:ServletRequest或者HttpServletRequest等;
- response:ServletResponse或者HttpServletResponse等;
- session:HttpSession等,如果加了此参数将强制存在相应的会话。因此,这个参数永远不会为空。但是,会话访问可能不是线程安全的,尤其是在 Servlet 环境中:如果允许多个请求同时访问一个会话,可以将"synceOnSession"标志切换为"true"。
- WebRequest或者NativeWebRequest:允许通用请求参数访问以及请求/会话属性访问,而无需绑定到 Servlet API。
- Locale:本地化信息
- InputStream或者Reader: Servlet API 暴露的原始 InputStream/Reader。
- OutputStream或者Writer:Servlet API 暴露的原始 OutputStream/Writer。
- Model:模型作为从处理程序方法返回模型映射的替代方法。注意,提供的模型未使用常规模型属性预先填充,因此始终为空,以便为特定于异常的视图准备模型。
@ExceptionHandler注解的方法的返回可以是:
- ModelAndView对象;
- Model对象:其视图名称通过 org.springframework.web.servlet.RequestToViewNameTranslator 隐式确定
- Map对象:暴露的视图,其视图名称通过 org.springframework.web.servlet.RequestToViewNameTranslator 隐式确定
- View对象;
- String:视图名称
- @ResponseBody
- HttpEntity> 或者 ResponseEntity> 对象:对象(仅限 Servlet)来设置响应标头和内容。响应实体正文将使用消息转换器进行转换并写入响应流
- void:参数中加response,可以手动将响应设置进response。
@Component @ControllerAdvice public class GlobalExceptionHandler { @Resource private MessageProcessor messageProcessor; @ExceptionHandler(value = Exception.class) @ResponseBody @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) public String handleException(HttpServletRequest request, Exception exception) { return messageProcessor.getMessage(request, INTERNAL_ERROR_DEFAULT_MESSAGE); } }
测试
@RequestMapping(value = "/user/{user_id}", method = RequestMethod.DELETE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody @ResponseStatus(code = HttpStatus.OK) public String deleteUser(HttpServletRequest request, HttpServletResponse response, @PathVariable(value = "user_id") String userId) { String userName = userService.getUserName(userId); if (userName == null) { throw new NullPointerException(); // return messageProcessor.getMessage(request, "response.400.message.0002", // "userId"); } // delete user return "success"; }
发现竟然乱码了,下面分析下原因
5.2.4 响应体返回中文乱码分析下原因,是因为触发了全局异常捕获,handleException()方法上标注了@ResponseBody,即返回的message会被放入响应体中返回给前端。和Controller方法不同的是,使用@RequestMapping方法指定了consumes=MediaType.APPLICATION_JSON_VALUE,即响应体会被json处理,但是@ExceptionHandler方法未指定响应体格式Content-Type,可以在postman中查看:
由SpringMVC处理流程可知
6、返回ModelAndView之后仍然是交由HandleAdapter去处理,所以重点分析下Adapter。这里的Adapter实现类为RequestMappingHandlerAdapter,入口为handleInternal方法,调用invokeHandlerMethod()
- handleInternal()
@Override protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; mav = invokeHandlerMethod(request, response, handlerMethod); return mav; }
- invokeHandlerMethod()
@Nullable protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { // 创建ServletWebRequest对象 ServletWebRequest webRequest = new ServletWebRequest(request, response); try { WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); // 设置方法参数解析器 if (this.argumentResolvers != null) { invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } // 设置方法返回值处理器 if (this.returnValueHandlers != null) { invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); } invocableMethod.setDataBinderFactory(binderFactory); invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); // ModelAndView容器,将上述参数设置进去并初始化相关配置 ModelAndViewContainer mavContainer = new ModelAndViewContainer(); mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); modelFactory.initModel(webRequest, mavContainer, invocableMethod); mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); // 异步请求处理AsyncWebRequest,不涉及已忽略 // 调用此方法并处理返回值 invocableMethod.invokeAndHandle(webRequest, mavContainer); return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); } }
- invokeAndHandle()
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 调用方法获取方法返回值,如果发生异常,此时获取的是异常处理的方法的返回值 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); // 根据@ResponseStatus注解设置响应码 setResponseStatus(webRequest); // 返回值为null时处理 if (returnValue == null) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { disableContentCachingIfNecessary(webRequest); mavContainer.setRequestHandled(true); return; } } else if (StringUtils.hasText(getResponseStatusReason())) { // 如果在@ResponseStatus注解设置reason(),则进去此处 mavContainer.setRequestHandled(true); return; } mavContainer.setRequestHandled(false); Assert.state(this.returnValueHandlers != null, "No return value handlers"); // returnValueHandlers处理返回值 try { this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(formatErrorForReturnValue(returnValue), ex); } throw ex; } }
- handleReturnValue()
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { // 在returnHandler列表中根据supportsReturnType()方法,获取第一个支持的Handler HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); if (handler == null) { throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName()); } // Handler中有messageConverters列表,根据messageConverter的canWrite()方法选择合适的messageConvert,并将message写入到response中。 handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); }
由上分析可知,最终是根据messageConvert将返回值写入到response中。由于返回值是String,而messageConvert所以会使用StringHttpMessageConvert:
而StringHttpMessageConvert的Content-Type默认是text/plain;charset=UTF-8,编码方式是ISO-8859-1,所以产生中文乱码
-
解决方法一:在配置文件中指定StringHttpMessageConverter的字符集,推荐此方案。
text/plain;charset=utf-8 text/html;charset=UTF-8 -
解决方法二:在bean后处理器中指定StringHttpMessageConverter的字符集,所有的被spring托管的bean都会执行postProcessAfterInitialization方法,建议使用解决方法一。
@Component public class DefineCharSet implements BeanPostProcessor { //实例化之前调用 @Nullable public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } //实例化之后调用 @Nullable public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // 指定StringHttpMessageConverter的字符集 if(bean instanceof StringHttpMessageConverter){ MediaType mediaType = new MediaType("text", "html", Charset.forName("UTF-8")); List
types = new ArrayList (); types.add(mediaType); ((StringHttpMessageConverter) bean).setSupportedMediaTypes(types); } return bean; } }
测试:
5.3 Spring国际化整合Hibernate参数校验 5.3.1 LocalValidatorFactoryBean配置validationMessageSource5.3.2 三种常见参数校验异常 5.3.2.1 BindException
BindException,仅对于表单提交的请求体body校验有效(@Validated 或@Valid注解),校验失败会抛出此类异常,对于以json格式提交将会失效。
5.3.2.2 MethodArgumentNotValidExceptionMethodArgumentNotValidException是BindException的子类,提交的请求体body为json格式时有效,校验失败是会抛出身份校验异常。
5.3.2.3 ConstraintViolationExceptionSpring的校验能力(@Validated加MethodValidationPostProcessor,参考4.4 Spring的校验机制以及@Validated注解的使用)会抛出此种异常。对请求参数(@RequestParam)和路径参数(@PathVariable)有效。
5.3.3 国际化错误信息前面章节4.3.2 hibernate-validator常用注解中参数校验注解message信息都由一个默认值,如@NotNull注解(validation-api包的注解)所示:
如果在校验注解上重新指定message,则可以把默认的message这些值国际化,
- messages_zh_CN.properties
response.400.message.0001=请求参数不合法。 response.400.message.0002=请求参数 {0} 不合法。 response.404.message.0001=资源不存在。 response.500.message.0001=系统错误,请稍后重试。 # 参数校验默认国际化key值 javax.validation.constraints.NotNull.message=请求参数 %s 不能为null。 javax.validation.constraints.NotBlank.message=请求参数 %s 不能为空。 javax.validation.constraints.Min.message=请求参数 %s 不能小于{value}。 javax.validation.constraints.Max.message=请求参数 %s 不能大于{value}。 javax.validation.constraints.Size.message=请求参数 %s 长度(或数量)必须在{min}和{max}之间。 javax.validation.constraints.Pattern.message=请求参数 %s 不满足正则规则{regexp}。 org.hibernate.validator.constraints.Length.message=请求参数 %s 长度必须在{min}和{max}之间。 org.hibernate.validator.constraints.Range.message=请求参数 %s 必须在{min}和{max}之间。
- message_en_US.properties
# 英文 response.400.message.0001=Request parameters are invalid. response.400.message.0002=Request parameters {0} are invalid. response.404.message.0001=Request resource can not be found. response.500.message.0001=System error, please try again later. # 参数校验默认国际化key值 javax.validation.constraints.NotNull.message=The request parameter %s cannot be null. javax.validation.constraints.NotBlank.message=The request parameter %s cannot be blank. javax.validation.constraints.Min.message=Request parameter %s cannot be less than {value}. javax.validation.constraints.Max.message=Request parameter %s cannot be greater than {value}. javax.validation.constraints.Size.message=The request parameter %s length (or quantity) must be between {min} and {max}. javax.validation.constraints.Pattern.message=Request parameter %s does not satisfy regular rule {regexp}. org.hibernate.validator.constraints.Length.message=The request parameter %s length must be between {min} and {max}. org.hibernate.validator.constraints.Range.message=The request parameter %s must be between {min} and {max}
这里{value}、{min}和{max}在国际化的时候会将注解内的值替换,比如@Min和@Max的value()在国际化是会替换javax.validation.constraints.Min.message=请求参数 %s 不能小于{value}。中的{value}。这里也一并将参数的名称放入国际化消息中,可以使用%s,在返回错误信息之前使用String.format()方法将参数名放入国际化后的消息。
5.3.4 全局异常处理package org.numb.common.handler; import static org.numb.common.i18n.ErrorCode.INTERNAL_ERROR_DEFAULT_MESSAGE; import static org.numb.common.i18n.ErrorCode.INVALID_PARAMETERS_MESSAGE; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.validation.ConstraintViolationException; import org.apache.commons.lang3.StringUtils; import org.numb.common.i18n.MessageProcessor; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.validation.BindException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; @Component @ControllerAdvice public class GlobalExceptionHandler { @Resource private MessageProcessor messageProcessor; @ExceptionHandler(value = {BindException.class, MethodArgumentNotValidException.class}) @ResponseBody @ResponseStatus(value = HttpStatus.BAD_REQUEST) public String handleBindException(HttpServletRequest request, BindException exception) { String message = ""; String fieldName = ""; // 从exception中获取参数名称fieldName if (exception.getBindingResult().getFieldError() != null) { String field = exception.getBindingResult().getFieldError().getField(); if (StringUtils.isNotBlank(field)) { fieldName = field; } } if (exception.getBindingResult().getAllErrors().size() > 0) { message = exception.getBindingResult().getAllErrors().get(0).getDefaultMessage(); } // 如果国际化消息为空,使用默认的国际化消息 if (StringUtils.isBlank(message)) { message = messageProcessor.getMessage(request, INVALID_PARAMETERS_MESSAGE); } return String.format(message, fieldName); } @ExceptionHandler(value = ConstraintViolationException.class) @ResponseBody @ResponseStatus(value = HttpStatus.BAD_REQUEST) public String handleConstraintViolationException(HttpServletRequest request, ConstraintViolationException exception) { return exception.getMessage(); } @ExceptionHandler(value = Exception.class) @ResponseBody @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) public String handleException(HttpServletRequest request, Exception exception) { return messageProcessor.getMessage(request, INTERNAL_ERROR_DEFAULT_MESSAGE); } }
测试
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)