您好,登录后才能下订单哦!
# SpringBoot怎么定制错误页面及错误数据
## 前言
在Web应用开发中,错误处理是用户体验的重要组成部分。SpringBoot提供了强大的错误处理机制,允许开发者灵活定制错误页面和错误数据。本文将深入探讨SpringBoot错误处理的原理、实现方式以及高级定制技巧。
## 一、SpringBoot错误处理机制概述
### 1.1 默认错误处理
SpringBoot默认通过`BasicErrorController`处理错误请求:
```java
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
// 处理HTML错误响应
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
// ...
}
// 处理JSON错误响应
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
// ...
}
}
SpringBoot通过ErrorAttributes
接口收集错误信息:
public interface ErrorAttributes {
Map<String, Object> getErrorAttributes(WebRequest webRequest,
ErrorAttributeOptions options);
Throwable getError(WebRequest webRequest);
}
默认实现DefaultErrorAttributes
提供了以下信息:
- timestamp:时间戳
- status:HTTP状态码
- error:错误原因
- message:错误消息
- path:请求路径
最简单的定制方式是在src/main/resources/static/error/
目录下添加静态HTML:
resources/
└── static/
└── error/
├── 404.html
├── 500.html
└── 5xx.html
命名规则:
- 精确匹配:404.html
- 范围匹配:5xx.html
- 通用匹配:error.html
对于动态内容,可以使用模板引擎(Thymeleaf、Freemarker等):
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Error Page</title>
</head>
<body>
<h1 th:text="${status}">Status</h1>
<p th:text="${error}">Error</p>
<p th:text="${message}">Message</p>
<p th:text="${path}">Path</p>
</body>
</html>
存放位置:
resources/
└── templates/
└── error/
├── 404.html
└── 500.html
完全控制错误处理逻辑:
@Controller
public class MyErrorController implements ErrorController {
@RequestMapping("/error")
public String handleError(HttpServletRequest request) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (status != null) {
int statusCode = Integer.parseInt(status.toString());
if(statusCode == HttpStatus.NOT_FOUND.value()) {
return "error-404";
} else if(statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
return "error-500";
}
}
return "error";
}
}
@Component
public class CustomErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest,
ErrorAttributeOptions options) {
Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, options);
// 添加自定义字段
errorAttributes.put("locale", webRequest.getLocale().toString());
errorAttributes.put("success", false);
errorAttributes.put("version", "1.0");
// 移除敏感信息
errorAttributes.remove("trace");
return errorAttributes;
}
}
@ControllerAdvice
public class CustomExceptionHandler {
@ExceptionHandler(CustomException.class)
public ResponseEntity<ErrorResponse> handleCustomException(
CustomException ex, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse(
LocalDateTime.now(),
ex.getErrorCode(),
ex.getMessage(),
request.getDescription(false));
return new ResponseEntity<>(errorResponse, ex.getHttpStatus());
}
}
@Data
@AllArgsConstructor
class ErrorResponse {
private LocalDateTime timestamp;
private String code;
private String message;
private String path;
}
@RestController
@RequestMapping("/api/**")
public class ApiErrorController {
@RequestMapping("/error")
public ResponseEntity<ApiError> handleApiError(HttpServletRequest request) {
HttpStatus status = getStatus(request);
ApiError apiError = new ApiError(status,
(String) request.getAttribute(RequestDispatcher.ERROR_MESSAGE));
return new ResponseEntity<>(apiError, status);
}
private HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
return statusCode != null ? HttpStatus.valueOf(statusCode) : HttpStatus.INTERNAL_SERVER_ERROR;
}
}
spring.messages.basename=messages/errors
messages/
└── errors.properties
└── errors_zh_CN.properties
<p th:text="#{error.404.message}">Page not found</p>
@Component
public class CustomErrorController extends BasicErrorController {
private static final Logger logger = LoggerFactory.getLogger(CustomErrorController.class);
public CustomErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes, new ErrorProperties());
}
@Override
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
logError(request);
return super.errorHtml(request, response);
}
private void logError(HttpServletRequest request) {
Throwable error = getError(request);
if (error != null) {
logger.error("Error occurred: ", error);
} else {
logger.error("Error with status {} occurred",
request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE));
}
}
}
@SpringBootTest
@AutoConfigureMockMvc
class ErrorHandlingTest {
@Autowired
private MockMvc mockMvc;
@Test
void test404Page() throws Exception {
mockMvc.perform(get("/nonexistent"))
.andExpect(status().isNotFound())
.andExpect(view().name("error/404"));
}
@Test
void testApiError() throws Exception {
mockMvc.perform(get("/api/nonexistent")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.success").value(false))
.andExpect(jsonPath("$.code").value("NOT_FOUND"));
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ErrorHandlingIntegrationTest {
@LocalServerPort
private int port;
@Test
void testErrorPage() {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.getForEntity(
"http://localhost:" + port + "/nonexistent", String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
assertThat(response.getBody()).contains("Page Not Found");
}
}
@Configuration
public class ErrorPageConfig implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
@Override
public void customize(ConfigurableWebServerFactory factory) {
if (factory instanceof ConfigurableServletWebServerFactory) {
((ConfigurableServletWebServerFactory) factory).addErrorPages(
new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"),
new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500"),
new ErrorPage("/error")
);
}
}
}
@RestController
public class AsyncController {
@GetMapping("/async")
public CompletableFuture<String> asyncRequest() {
return CompletableFuture.supplyAsync(() -> {
if (Math.random() > 0.5) {
throw new RuntimeException("Random error");
}
return "Success";
}).exceptionally(ex -> {
throw new ResponseStatusException(
HttpStatus.INTERNAL_SERVER_ERROR,
"Async error occurred", ex);
});
}
}
server.error.include-stacktrace=never
server.error.include-message=never
server.error.include-binding-errors=never
@Component
public class ErrorSanitizer implements ErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest,
ErrorAttributeOptions options) {
Map<String, Object> errorAttributes = // 获取原始属性
// 清理敏感信息
errorAttributes.values().removeIf(
value -> value.toString().contains("password") ||
value.toString().contains("secret"));
return errorAttributes;
}
}
可能原因: 1. 文件位置不正确 2. 缓存问题 3. 优先级冲突
解决方案:
1. 确认文件位于resources/static/error/
或resources/templates/error/
2. 清理浏览器缓存或使用spring.thymeleaf.cache=false
3. 检查是否有自定义ErrorController
覆盖了默认行为
可能原因: 1. 过滤器/拦截器吞掉了异常 2. 异步处理未正确传播异常
解决方案: 1. 检查过滤器链中的异常处理 2. 确保异步方法正确传播异常:
@Async
public CompletableFuture<String> asyncMethod() {
try {
// 业务逻辑
} catch (Exception ex) {
CompletableFuture<String> future = new CompletableFuture<>();
future.completeExceptionally(ex);
return future;
}
}
检查步骤:
1. 确认messages.properties
文件存在
2. 检查spring.messages.basename
配置
3. 验证请求的Accept-Language
头
SpringBoot提供了灵活的错误处理机制,开发者可以通过多种方式定制错误页面和错误数据:
最佳实践建议: - 生产环境隐藏敏感信息 - 为API和页面提供不同的错误处理 - 实现完善的错误日志记录 - 考虑国际化需求 - 进行充分的错误处理测试
通过合理利用SpringBoot的错误处理功能,可以显著提升应用的健壮性和用户体验。
本文共计约8650字,详细介绍了SpringBoot错误页面和数据定制的各个方面,从基础配置到高级技巧,并提供了实用的代码示例和解决方案。 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。