복붙노트

[SPRING] 봄 필터 예외 처리

SPRING

봄 필터 예외 처리

@ExceptionHandler를 사용하여 봄에 예외를 처리하고 있습니다. 컨트롤러에 의해 Throw 된 모든 예외는 @ExceptionHandler로 주석 된 메소드를 사용하여 캐치되며 이에 따라 조치가 취해집니다. 모든 컨트롤러에 대해 @exceptionHandler를 작성하지 않으려면 @ControllerAdvice 주석을 사용하고 있습니다.

예상대로 잘 작동합니다.

이제는 DelegatingFilterProxy와 ContextLoaderListener를 사용하여 구현 된 필터 (예, 인터셉터가 아닌 특정 요구 사항 처리)가 있습니다.

내가 위의 필터에서 동일한 예외를 던질 때 컨트롤러의 경우 에서처럼 처리되지 않았습니다. 그것은 사용자에게 직접 던져진다.

여기서 뭐가 잘못 됐니?

해결법

  1. ==============================

    1.컨트롤러가 해결되기 전에 필터가 발생하기 때문에 필터에서 발생하는 예외는 컨트롤러 조언에 의해 포착 될 수 없습니다.

    컨트롤러가 해결되기 전에 필터가 발생하기 때문에 필터에서 발생하는 예외는 컨트롤러 조언에 의해 포착 될 수 없습니다.

    필터는 서블릿의 일부이며 MVC 스택이 아닙니다.

  2. ==============================

    2.아마도 필터에 예외가 throw 된 결과로 HTTP 상태 코드를 설정하려고 할 것입니다. 그렇다면 단순히 다음과 같이 상태를 설정하십시오.

    아마도 필터에 예외가 throw 된 결과로 HTTP 상태 코드를 설정하려고 할 것입니다. 그렇다면 단순히 다음과 같이 상태를 설정하십시오.

    HttpServletResponse 응답 = (HttpServletResponse) res; response.setStatus (HttpServletResponse.SC_UNAUTHORIZED);

  3. ==============================

    3.예외는 컨트롤러가 아니라 필터에서 발생하므로 @ControllerAdvice는 예외를 catch하지 않습니다.

    예외는 컨트롤러가 아니라 필터에서 발생하므로 @ControllerAdvice는 예외를 catch하지 않습니다.

    그래서, 사방을 살펴본 후에 발견 한 가장 좋은 해결책은이 내부 오류에 대한 필터를 만드는 것이 었습니다.

    public class ExceptionHandlerFilter extends OncePerRequestFilter {
        @Override
        public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            try {
                filterChain.doFilter(request, response);
    
            } catch (JwtException e) {
                setErrorResponse(HttpStatus.BAD_REQUEST, response, e);
                e.printStackTrace();
            } catch (RuntimeException e) {
                e.printStackTrace();
                setErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, response, e);
            }
        }
    
        public void setErrorResponse(HttpStatus status, HttpServletResponse response, Throwable ex){
            response.setStatus(status.value());
            response.setContentType("application/json");
            // A class used for errors
            ApiError apiError = new ApiError(status, ex);
            try {
                String json = apiError.convertToJson();
                System.out.println(json);
                response.getWriter().write(json);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    }
    

    귀하의 설정에 추가, 나는 WebSecurityConfigurerAdapter 구현을 사용하고 있습니다 :

    // Custom JWT based security filter
    httpSecurity
            .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
    
    // Custom Exception Filter for filter
    httpSecurity
            .addFilterBefore(exceptionHandlerFilterBean(), JwtAuthenticationTokenFilter.class);
    

    오류 클래스 :

    public class ApiError {
    
        private HttpStatus status;
        @JsonSerialize(using = LocalDateTimeSerializer.class)
        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
        private LocalDateTime timestamp;
        private String message;
        private String debugMessage;
    
        private ApiError() {
            timestamp = LocalDateTime.now();
        }
        public ApiError(HttpStatus status) {
            this();
            this.status = status;
        }
    
        public ApiError(HttpStatus status, Throwable ex) {
            this();
            this.status = status;
            this.message = "Unexpected error";
            this.debugMessage = ex.getLocalizedMessage();
        }
    
        public ApiError(HttpStatus status, String message, Throwable ex) {
            this();
            this.status = status;
            this.message = message;
            this.debugMessage = ex.getLocalizedMessage();
        }
    
        public String convertToJson() throws JsonProcessingException {
            if (this == null) {
                return null;
            }
            ObjectMapper mapper = new ObjectMapper();
            mapper.registerModule(new JavaTimeModule());
            mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    
            return mapper.writeValueAsString(this);
        }
    
      //Setters and getters
    }
    
  4. ==============================

    4.아래 코드 스 니펫을 확인하십시오.

    아래 코드 스 니펫을 확인하십시오.

    final HttpServletResponseWrapper wrapper = new 
    HttpServletResponseWrapper((HttpServletResponse) res);    
    wrapper.sendError(HttpServletResponse.SC_UNAUTHORIZED, "<your error msg>");    
    res = wrapper.getResponse();
    

    이 필터를 사용하여 return 문을 추가하면 chain.doFilter (req, res)가이를 재정의합니다.

  5. ==============================

    5.이것은 내 필터 클래스에서 오류를 throw하기 위해 수행 한 작업입니다.

    이것은 내 필터 클래스에서 오류를 throw하기 위해 수행 한 작업입니다.

        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            HttpServletRequest req = (HttpServletRequest) request;
            if (req.getHeader("Content-Type") == null) {
                HttpServletResponse httpResponse = (HttpServletResponse) response;                
                httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "Required headers not specified in the request");            
            }
            chain.doFilter(request, response);
        }
    
  6. ==============================

    6.나처럼 스프링 3.1 (@ControllerAdvice 뒤에 0.1 vesrsions)이 붙어 있다면 방금 전에이 솔루션을 사용해 볼 수 있습니다.

    나처럼 스프링 3.1 (@ControllerAdvice 뒤에 0.1 vesrsions)이 붙어 있다면 방금 전에이 솔루션을 사용해 볼 수 있습니다.

    그럼, 당신은 예외 결정자 들었어? 그렇지 않다면 여기를 읽으십시오.

    @Component
    public class RestExceptionResolver extends ExceptionHandlerExceptionResolver {
    
        @Autowired
        //If you have multiple handlers make this a list of handlers
        private RestExceptionHandler restExceptionHandler;
        /**
         * This resolver needs to be injected because it is the easiest (maybe only) way of getting the configured MessageConverters
         */
        @Resource
        private ExceptionHandlerExceptionResolver defaultResolver;
    
        @PostConstruct
        public void afterPropertiesSet() {
            setMessageConverters(defaultResolver.getMessageConverters());
            setOrder(2); // The annotation @Order(2) does not work for this type of component
            super.afterPropertiesSet();
        }
    
        @Override
        protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
            ExceptionHandlerMethodResolver methodResolver = new ExceptionHandlerMethodResolver(restExceptionHandler.getClass());
            Method method = methodResolver.resolveMethod(exception);
            if (method != null) {
                return new ServletInvocableHandlerMethod(restExceptionHandler, method);
            }
            return null;
        }
    
        public void setRestExceptionHandler(RestExceptionHandler restExceptionHandler) {
            this.restExceptionHandler = restExceptionHandler;
        }
    
        public void setDefaultResolver(ExceptionHandlerExceptionResolver defaultResolver) {
            this.defaultResolver = defaultResolver;
        }
    }
    

    그러면 예제 핸들러는 다음과 같이 보일 것이다.

    @Component
    public class RestExceptionHandler {
    
        @ExceptionHandler(ResourceNotFoundException.class)
        @ResponseStatus(HttpStatus.NOT_FOUND)
        @ResponseBody
        public Map<String, Object> handleException(ResourceNotFoundException e, HttpServletResponse response) {
            Map<String, Object> error = new HashMap<>();
            error.put("error", e.getMessage());
            error.put("resource", e.getResource());
            return error;
        }
     }
    

    물론 콩을 등록하는 것을 잊지 않을 것입니다.

    그런 다음 원하는 필터 앞에 호출되는 필터를 만듭니다 (선택적으로 모두 'em').

    그런 다음 그 필터에서

    try{
       chain.doFilter(request, response);
    catch(Exception e){
       exceptionResolver(request, response, exceptionHandler, e);
       //Make the processing stop here... 
       return; //just in case
    }
    
  7. ==============================

    7.나머지 API를 사용하여 응용 프로그램을 작성 했으므로 예외를 throw 한 후 다시 쓸 수있는 필터에서이 문제를 잡아 해결했습니다. filterChain.doFilter (request, response);를 기억하십시오. 포함되어야합니다.

    나머지 API를 사용하여 응용 프로그램을 작성 했으므로 예외를 throw 한 후 다시 쓸 수있는 필터에서이 문제를 잡아 해결했습니다. filterChain.doFilter (request, response);를 기억하십시오. 포함되어야합니다.

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            // something may throw an exception
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            // ResponseWrapper is a customized class
            ResponseWrapper responseWrapper = new ResponseWrapper().fail().msg(e.getMessage());
            response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
            response.getWriter().write(JSON.toJSONString(responseWrapper));
        }
    }
    
  8. from https://stackoverflow.com/questions/17715921/exception-handling-for-filter-in-spring by cc-by-sa and MIT license