2014-05-10 26 views
34

Tôi đang viết một ứng dụng web với Spring 4.0.4 và Spring Boot 1.0.2 bằng Tomcat dưới dạng vùng chứa web nhúng và tôi muốn thực hiện xử lý ngoại lệ toàn cục chặn tất cả các ngoại lệ và ghi lại chúng theo một cách cụ thể. Các yêu cầu đơn giản của tôi là:Cách viết một trình xử lý lỗi toàn cục thích hợp với Spring MVC/Spring Boot

  • Tôi muốn xử lý toàn bộ tất cả các trường hợp ngoại lệ chưa được xử lý ở nơi nào khác (Trong trình xử lý ngoại lệ của trình điều khiển). Tôi muốn đăng nhập tin nhắn và tôi muốn hiển thị thông báo lỗi tùy chỉnh cho người dùng.
  • Tôi không muốn Spring hoặc vùng chứa web tự ghi lại bất kỳ lỗi nào vì tôi muốn tự mình làm điều này.

Cho đến nay giải pháp của tôi trông như thế này (Giản thể, không khai thác gỗ và không có chuyển hướng đến một cái nhìn lỗi):

@Controller 
@RequestMapping("/errors") 
public class ErrorHandler implements EmbeddedServletContainerCustomizer 
{ 
    @Override 
    public void customize(final ConfigurableEmbeddedServletContainer factory) 
    { 
     factory.addErrorPages(new ErrorPage("/errors/unexpected")); 
     factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/errors/notfound")); 
    } 

    @RequestMapping("unexpected") 
    @ResponseBody 
    public String unexpectedError(final HttpServletRequest request) 
    { 
     return "Exception: " + request.getAttribute("javax.servlet.error.exception"); 
    } 

    @RequestMapping("notfound") 
    @ResponseBody 
    public String notFound() 
    { 
     return "Error 404"; 
    } 
} 

Kết quả là trường hợp ngoại lệ ném vào bộ điều khiển được xử lý một cách chính xác bằng phương pháp unexpectedError và Mã trạng thái 404 được xử lý theo phương thức notFound. Cho đến nay rất tốt, nhưng tôi có các vấn đề sau:

  • Tomcat hoặc Spring (không chắc ai chịu trách nhiệm) vẫn đang ghi nhật ký thông báo lỗi. Tôi không muốn điều đó bởi vì tôi muốn tự mình đăng nhập (với thông tin bổ sung) và tôi không muốn các thông báo lỗi trùng lặp trong nhật ký. Làm thế nào tôi có thể ngăn chặn đăng nhập mặc định này?
  • Cách tôi truy cập đối tượng ngoại lệ có vẻ không đúng. Tôi tìm nạp nếu thuộc tính yêu cầu javax.servlet.error.exception. Và đó không phải là ngoại lệ ném, nó là một ví dụ của org.springframework.web.util.NestedServletException và tôi phải đi sâu vào ngoại lệ lồng nhau này để lấy một thực tế. Tôi khá chắc chắn có một cách dễ dàng hơn nhưng tôi không thể tìm thấy nó.

Vậy làm cách nào để tôi có thể giải quyết những vấn đề này? Hoặc có lẽ cách tôi thực hiện xử lý ngoại lệ toàn cầu này là hoàn toàn sai và có một lựa chọn tốt hơn?

Trả lời

51

Có một cái nhìn tại ControllerAdvice Bạn có thể làm một cái gì đó như thế này:

@ControllerAdvice 
public class ExceptionHandlerController { 

    public static final String DEFAULT_ERROR_VIEW = "error"; 

    @ExceptionHandler(value = {Exception.class, RuntimeException.class}) 
    public ModelAndView defaultErrorHandler(HttpServletRequest request, Exception e) { 
      ModelAndView mav = new ModelAndView(DEFAULT_ERROR_VIEW); 

     mav.addObject("datetime", new Date()); 
     mav.addObject("exception", e); 
     mav.addObject("url", request.getRequestURL()); 
     return mav; 
    } 
} 
+0

Cảm ơn, này hoạt động tuyệt vời. Nhưng bây giờ tôi phát hiện ra một yêu cầu khác không phải là một phần của câu hỏi ban đầu của tôi vì vậy tôi đã tạo ra một câu hỏi mới. Có thể bạn cũng có thể trợ giúp ở đó: http://stackoverflow.com/questions/23582534/how-to-handle-exceptions-in-spring-mvc-differently-for-html-and-json-requests – kayahr

+1

Giải pháp sạch nhất hiện có. Một sự bổ sung đáng giá khác có thể là @ResponseStatus đến 500, điều này sẽ trả về phản hồi trong HTTP 500 như mong đợi. –

+3

Xin lưu ý rằng giải pháp này cũng sẽ bắt các ngoại lệ của Spring Security như AuthenticationException. Do đó, chuyển hướng đến tài nguyên được bảo vệ sau khi đăng nhập sẽ không hoạt động. Giải thích: nhập tài nguyên được bảo vệ thường chuyển hướng bạn đến trang đăng nhập. Sau khi đăng nhập, bạn nên quay trở lại tài nguyên được bảo vệ (Spring Security đảm nhiệm điều đó bằng cách bắt AuthenticationException), nhưng khi bạn bắt tất cả các ngoại lệ như vậy, Spring Security sẽ không bắt được nó nữa. – Kacper86

Các vấn đề liên quan