# 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進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。