九、SpringBootWeb开发-国际化与错误处理
# 1. 国际化
国际化的自动配置参照MessageSourceAutoConfiguration
实现步骤:
Spring Boot 在类路径根下查找messages资源绑定文件。文件名为:messages.properties
多语言可以定义多个消息文件,命名为
messages_区域代码.properties
。如:messages.properties
:默认messages_zh_CN.properties
:中文环境messages_en_US.properties
:英语环境
在程序中可以自动注入
MessageSource
组件,获取国际化的配置项值在页面中可以使用表达式
#{}
获取国际化的配置项值,默认就配置好了,直接用
@Autowired //国际化取消息用的组件
MessageSource messageSource;
@GetMapping("/haha")
public String haha(HttpServletRequest request){
Locale locale = request.getLocale();
//利用代码的方式获取国际化配置文件中指定的配置项的值
String login = messageSource.getMessage("login", null, locale);
return login;
}
2
3
4
5
6
7
8
9
10
# 2. 错误处理
# 1. 默认机制
错误处理的自动配置都在
ErrorMvcAutoConfiguration
中,两大核心机制:
- SpringBoot 会自适应处理错误,响应页面或JSON数据
- SpringMVC的错误处理机制依然保留,MVC处理不了,才会交给boot进行处理
如果在Controller中存在标注了@ExceptionHandler
的方法,那么当这个Controller出现错误时,该方法会被调用:
@ResponseBody
@ExceptionHandler(Exception.class)
public String handleException(Exception e) {
return "Ohho~~~, 原因:" + e.getMessage();
}
2
3
4
5
标注了@ControllerAdvice
注解的Controller可以处理所有Controller发生的错误
@ControllerAdvice
public class GlobalExceptionHandler {
@ResponseBody
@ExceptionHandler(Exception.class)
public String handleException(Exception e) {
return "Ohho~~~统一处理, 原因:" + e.getMessage();
}
}
2
3
4
5
6
7
8
- 发生错误以后,转发给/error路径,SpringBoot在底层写好一个
BasicErrorController
的组件,专门处理这个请求
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE) //返回HTML
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections
.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
@RequestMapping //返回 ResponseEntity, JSON
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
HttpStatus status = getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity<>(status);
}
Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
return new ResponseEntity<>(body, status);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- 错误页面是按照如下方式解析到的
//1、解析错误的自定义视图地址
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
//2、如果解析不到错误页面的地址,默认的错误页就是 error
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
2
3
4
容器中专门有一个错误视图解析器,用来解析发生错误后需要去哪个页面
@Bean
@ConditionalOnBean(DispatcherServlet.class)
@ConditionalOnMissingBean(ErrorViewResolver.class)
DefaultErrorViewResolver conventionErrorViewResolver() {
return new DefaultErrorViewResolver(this.applicationContext, this.resources);
}
2
3
4
5
6
SpringBoot解析自定义错误页的默认规则
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
// 根据错误状态码解析错误页面的精确值
ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
}
return modelAndView;
}
// 默认在classpath://template/error/路径下查找特定的错误页面
private ModelAndView resolve(String viewName, Map<String, Object> model) {
String errorViewName = "error/" + viewName;
// 根据是否存在模板引擎有不同的处理方式
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
this.applicationContext);
if (provider != null) {
return new ModelAndView(errorViewName, model);
}
return resolveResource(errorViewName, model);
}
private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
for (String location : this.resources.getStaticLocations()) {
try {
Resource resource = this.applicationContext.getResource(location);
resource = resource.createRelative(viewName + ".html");
if (resource.exists()) {
return new ModelAndView(new HtmlResourceView(resource), model);
}
}
catch (Exception ex) {
}
}
return null;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
容器中有一个默认的名为 error 的 view; 提供了默认白页功能
@Bean(name = "error")
@ConditionalOnMissingBean(name = "error")
public View defaultErrorView() {
return this.defaultErrorView;
}
2
3
4
5
封装了JSON格式的错误信息
@Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {
return new DefaultErrorAttributes();
}
2
3
4
5
规则:
解析一个错误页
如果发生了500、404、503、403 这些错误
- 如果有模板引擎,默认在
classpath:/templates/error/**精确码.html**
- 如果没有模板引擎,在静态资源文件夹下找
**精确码.html**
- 如果有模板引擎,默认在
如果匹配不到
精确码.html
这些精确的错误页,就去找5xx.html
,4xx.html
模糊匹配- 如果有模板引擎,默认在
classpath:/templates/error/5xx.html
- 如果没有模板引擎,在静态资源文件夹下找
5xx.html
- 如果有模板引擎,默认在
如果模板引擎路径
templates
下有error.html
页面,就直接渲染
# 2. 自定义错误响应
# 1. 自定义json响应
使用@ControllerAdvice
+ @ExceptionHandler
进行统一异常处理
# 2. 自定义页面响应
根据boot的错误页面规则,自定义页面模板
# 3. 最佳实战
前后分离
- 后台发生的所有错误,
@ControllerAdvice + @ExceptionHandler
进行统一异常处理。
- 后台发生的所有错误,
服务端页面渲染
不可预知的一些,HTTP码表示的服务器或客户端错误
给
classpath:/templates/error/
下面,放常用精确的错误码页面。500.html
,404.html
给
classpath:/templates/error/
下面,放通用模糊匹配的错误码页面。5xx.html
,4xx.html
发生业务错误
核心业务,每一种错误,都应该代码控制,跳转到自己定制的错误页。
通用业务,
classpath:/templates/error.html
页面,显示错误信息。
页面,JSON,可用的Model数据如下