[SPRING] Spring에서 필터에 던져진 예외를 관리하는 방법?
SPRINGSpring에서 필터에 던져진 예외를 관리하는 방법?
5xx 오류 코드를 관리하기 위해 일반적인 방법을 사용하고 싶습니다. db가 내 전체 봄 응용 프로그램에서 작동하지 않는 경우를 예로 들어 봅시다. 나는 스택 추적 대신에 꽤 오류 json을 원한다.
컨트롤러의 경우 다른 예외에 대한 @ControllerAdvice 클래스가 있으며 이는 요청의 중간에 db가 중지되는 경우를 잡는 중입니다. 그러나 이것이 전부는 아닙니다. OncePerRequestFilter를 확장하는 커스텀 CorsFilter가 있고 거기에서 doFilter를 호출 할 때 CannotGetJdbcConnectionException이 발생하고 @ControllerAdvice가 관리하지 않습니다. 온라인으로 여러 가지를 읽었을 때 혼란 스러웠습니다.
그래서 나는 많은 질문을 가지고있다 :
이 문제를 해결할 수있는 좋은 방법이 있습니까? 감사
해결법
-
==============================
1.이것이 내가 한 일입니다.
이것이 내가 한 일입니다.
여기서 필터에 대한 기본 사항을 읽었을 때 필터 체인에서 첫 번째가 될 사용자 지정 필터를 만들어야한다는 것을 알았고 거기에서 발생할 수있는 모든 런타임 예외를 잡으려고 시도합니다. 그렇다면 json을 수동으로 생성하여 응답에 넣어야합니다.
여기 내 맞춤 필터가 있습니다.
public class ExceptionHandlerFilter extends OncePerRequestFilter { @Override public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { filterChain.doFilter(request, response); } catch (RuntimeException e) { // custom error response class used across my project ErrorResponse errorResponse = new ErrorResponse(e); response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); response.getWriter().write(convertObjectToJson(errorResponse)); } } public String convertObjectToJson(Object object) throws JsonProcessingException { if (object == null) { return null; } ObjectMapper mapper = new ObjectMapper(); return mapper.writeValueAsString(object); } }
그리고 나서 CorsFilter 앞에 web.xml에 추가했습니다. 그리고 그것은 작동합니다!
<filter> <filter-name>exceptionHandlerFilter</filter-name> <filter-class>xx.xxxxxx.xxxxx.api.controllers.filters.ExceptionHandlerFilter</filter-class> </filter> <filter-mapping> <filter-name>exceptionHandlerFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>CorsFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>CorsFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
-
==============================
2.나는이 문제를 직접 다루었으며 아래의 단계를 수행하여 등록 된 필터에서 Throw 된 예외에 대해 @ControllerAdvise로 주석 된 ExceptionController를 재사용했습니다.
나는이 문제를 직접 다루었으며 아래의 단계를 수행하여 등록 된 필터에서 Throw 된 예외에 대해 @ControllerAdvise로 주석 된 ExceptionController를 재사용했습니다.
분명히 예외를 처리 할 수있는 많은 방법이 있지만 내 경우에는 ExceptionController에서 예외를 처리하기를 원했습니다. 왜냐하면 저는 고집스럽고 동일한 코드를 복사 / 붙여 넣기를 원하지 않기 때문에 (즉, 일부 처리 / ExceptionController의 코드 로깅). 필자는 필터가 아닌 나머지 JSON 응답을 반환하고 싶습니다.
{ "status": 400, "message": "some exception thrown when executing the request" }
어쨌든, 나는 ExceptionHandler를 사용하여 다음과 같이 약간의 추가 작업을해야했다.
단계
샘플 코드
//sample Filter, to be added in web.xml public MyFilterThatThrowException implements Filter { //Spring Controller annotated with @ControllerAdvise which has handlers //for exceptions private MyExceptionController myExceptionController; @Override public void destroy() { // TODO Auto-generated method stub } @Override public void init(FilterConfig arg0) throws ServletException { //Manually get an instance of MyExceptionController ApplicationContext ctx = WebApplicationContextUtils .getRequiredWebApplicationContext(arg0.getServletContext()); //MyExceptionHanlder is now accessible because I loaded it manually this.myExceptionController = ctx.getBean(MyExceptionController.class); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; try { //code that throws exception } catch(Exception ex) { //MyObject is whatever the output of the below method MyObject errorDTO = myExceptionController.handleMyException(req, ex); //set the response object res.setStatus(errorDTO .getStatus()); res.setContentType("application/json"); //pass down the actual obj that exception handler normally send ObjectMapper mapper = new ObjectMapper(); PrintWriter out = res.getWriter(); out.print(mapper.writeValueAsString(errorDTO )); out.flush(); return; } //proceed normally otherwise chain.doFilter(request, response); } }
그리고 이제 정상적인 경우 예외를 처리하는 샘플 Spring Controller (즉, 필터 수준에서 일반적으로 throw되지 않는 예외, 즉 필터에서 throw 된 예외에 대해 사용하려는 예외)
//sample SpringController @ControllerAdvice public class ExceptionController extends ResponseEntityExceptionHandler { //sample handler @ResponseStatus(value = HttpStatus.BAD_REQUEST) @ExceptionHandler(SQLException.class) public @ResponseBody MyObject handleSQLException(HttpServletRequest request, Exception ex){ ErrorDTO response = new ErrorDTO (400, "some exception thrown when " + "executing the request."); return response; } //other handlers }
Filter에서 Throw 된 Exception에 ExceptionController를 사용하고자하는 사람들과 솔루션 공유.
-
==============================
3.일반적인 방법을 원할 경우 web.xml에 오류 페이지를 정의 할 수 있습니다.
일반적인 방법을 원할 경우 web.xml에 오류 페이지를 정의 할 수 있습니다.
<error-page> <exception-type>java.lang.Throwable</exception-type> <location>/500</location> </error-page>
그리고 Spring MVC에서 매핑을 추가하십시오 :
@Controller public class ErrorController { @RequestMapping(value="/500") public @ResponseBody String handleException(HttpServletRequest req) { // you can get the exception thrown Throwable t = (Throwable)req.getAttribute("javax.servlet.error.exception"); // customize response to what you want return "Internal server error."; } }
-
==============================
4.이것은 기본 스프링 부트 / 에러 핸들러를 오버라이드하여 내 솔루션입니다.
이것은 기본 스프링 부트 / 에러 핸들러를 오버라이드하여 내 솔루션입니다.
package com.mypackage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.web.ErrorAttributes; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Map; /** * This controller is vital in order to handle exceptions thrown in Filters. */ @RestController @RequestMapping("/error") public class ErrorController implements org.springframework.boot.autoconfigure.web.ErrorController { private final static Logger LOGGER = LoggerFactory.getLogger(ErrorController.class); private final ErrorAttributes errorAttributes; @Autowired public ErrorController(ErrorAttributes errorAttributes) { Assert.notNull(errorAttributes, "ErrorAttributes must not be null"); this.errorAttributes = errorAttributes; } @Override public String getErrorPath() { return "/error"; } @RequestMapping public ResponseEntity<Map<String, Object>> error(HttpServletRequest aRequest, HttpServletResponse response) { RequestAttributes requestAttributes = new ServletRequestAttributes(aRequest); Map<String, Object> result = this.errorAttributes.getErrorAttributes(requestAttributes, false); Throwable error = this.errorAttributes.getError(requestAttributes); ResponseStatus annotation = AnnotationUtils.getAnnotation(error.getClass(), ResponseStatus.class); HttpStatus statusCode = annotation != null ? annotation.value() : HttpStatus.INTERNAL_SERVER_ERROR; result.put("status", statusCode.value()); result.put("error", statusCode.getReasonPhrase()); LOGGER.error(result.toString()); return new ResponseEntity<>(result, statusCode) ; } }
-
==============================
5.응용 프로그램의 상태를 테스트하고 문제가 발생하면 HTTP 오류를 반환 할 때 필터를 제안합니다. 아래 필터는 모든 HTTP 요청을 처리합니다. javax 필터가있는 Spring Boot에서 가장 짧은 솔루션.
응용 프로그램의 상태를 테스트하고 문제가 발생하면 HTTP 오류를 반환 할 때 필터를 제안합니다. 아래 필터는 모든 HTTP 요청을 처리합니다. javax 필터가있는 Spring Boot에서 가장 짧은 솔루션.
구현에서 다양한 조건이 될 수 있습니다. 내 경우에는 applicationManager가 응용 프로그램이 준비되었는지 테스트합니다.
import ...ApplicationManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class SystemIsReadyFilter implements Filter { @Autowired private ApplicationManager applicationManager; @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (!applicationManager.isApplicationReady()) { ((HttpServletResponse) response).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "The service is booting."); } else { chain.doFilter(request, response); } } @Override public void destroy() {} }
-
==============================
6.그래서, 위의 답변이 합쳐진 것을 기반으로 한 작업이 있습니다. 이미 @ControllerAdvice로 주석 된 GlobalExceptionHandler가 있었으며 필터에서 나온 예외를 처리하기 위해 해당 코드를 다시 사용할 수있는 방법을 찾고 싶었습니다.
그래서, 위의 답변이 합쳐진 것을 기반으로 한 작업이 있습니다. 이미 @ControllerAdvice로 주석 된 GlobalExceptionHandler가 있었으며 필터에서 나온 예외를 처리하기 위해 해당 코드를 다시 사용할 수있는 방법을 찾고 싶었습니다.
가장 간단한 해결책은 예외 처리기 만 남겨두고 다음과 같이 오류 컨트롤러를 구현하는 것입니다.
@Controller public class ErrorControllerImpl implements ErrorController { @RequestMapping("/error") public void handleError(HttpServletRequest request) throws Throwable { if (request.getAttribute("javax.servlet.error.exception") != null) { throw (Throwable) request.getAttribute("javax.servlet.error.exception"); } } }
따라서 예외로 인해 발생한 오류는 먼저 ErrorController를 통과하고 @Controller 컨텍스트 내에서 예외를 다시 처리하여 예외 처리기로 리디렉션됩니다 (다른 오류 (예외에 의해 직접적으로 발생하지 않음)는 수정하지 않고 ErrorController를 통과합니다 .
이것이 실제로 나쁜 생각 인 이유는 무엇입니까?
-
==============================
7.@ControllerAdvice가 작동해야하기 때문에 이상합니다. 올바른 Exception을 catch하고 있습니까?
@ControllerAdvice가 작동해야하기 때문에 이상합니다. 올바른 Exception을 catch하고 있습니까?
@ControllerAdvice public class GlobalDefaultExceptionHandler { @ResponseBody @ExceptionHandler(value = DataAccessException.class) public String defaultErrorHandler(HttpServletResponse response, DataAccessException e) throws Exception { response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); //Json return } }
또한 CorsFilter에서이 예외를 catch하고 500 오류를 보내려고합니다.
@ExceptionHandler(DataAccessException.class) @ResponseBody public String handleDataException(DataAccessException ex, HttpServletResponse response) { response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); //Json return }
from https://stackoverflow.com/questions/34595605/how-to-manage-exceptions-thrown-in-filters-in-spring by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] RestTemplate은 스레드로부터 안전한가요? (0) | 2018.12.12 |
---|---|
[SPRING] 객체 자체에서 AOP 프록시 가져 오기 (0) | 2018.12.12 |
[SPRING] '필드에서 찾을 수없는 유형의 bean이 필요합니다.' mongodb를 사용하여 오류 봄 편안한 API (0) | 2018.12.12 |
[SPRING] Spring REST 서비스 : 요청에서 JSON 검색하기 (0) | 2018.12.12 |
[SPRING] 스프링 보안 및 @Async (인증 된 사용자가 섞여 있음) (0) | 2018.12.12 |