จะจัดการข้อยกเว้นในตัวกรองในฤดูใบไม้ผลิได้อย่างไร?


119

ฉันต้องการใช้วิธีทั่วไปในการจัดการรหัสข้อผิดพลาด 5xx สมมติว่าเฉพาะกรณีเมื่อ db หยุดทำงานในแอปพลิเคชัน Spring ทั้งหมดของฉัน ฉันต้องการข้อผิดพลาด json แทนการติดตามสแต็ก

สำหรับคอนโทรลเลอร์ฉันมี@ControllerAdviceคลาสสำหรับข้อยกเว้นที่แตกต่างกันและนี่ก็เป็นการจับกรณีที่ db หยุดอยู่ตรงกลางของคำขอ แต่นี่ไม่ใช่ทั้งหมด. ฉันยังมีส่วนCorsFilterขยายที่กำหนดเองOncePerRequestFilterและที่นั่นเมื่อฉันโทรหาdoFilterฉันได้รับCannotGetJdbcConnectionExceptionและจะไม่ได้รับการจัดการโดยไฟล์@ControllerAdvice. ฉันอ่านหลายสิ่งทางออนไลน์ซึ่งทำให้ฉันสับสนมากขึ้นเท่านั้น

ดังนั้นฉันจึงมีคำถามมากมาย:

  • ฉันต้องติดตั้งตัวกรองแบบกำหนดเองหรือไม่? ฉันพบExceptionTranslationFilterแต่สิ่งนี้จัดการได้AuthenticationExceptionหรือAccessDeniedException.
  • ฉันคิดว่าจะใช้ของฉันเองHandlerExceptionResolverแต่ทำให้ฉันสงสัยฉันไม่มีข้อยกเว้นที่กำหนดเองในการจัดการต้องมีวิธีที่ชัดเจนกว่านี้ ฉันยังพยายามเพิ่ม try / catch และเรียกการใช้งานHandlerExceptionResolver(ควรจะดีพอข้อยกเว้นของฉันไม่มีอะไรพิเศษ) แต่สิ่งนี้ไม่ส่งคืนอะไรเลยในการตอบกลับฉันได้รับสถานะ 200 และเนื้อความว่างเปล่า

มีวิธีใดที่ดีในการจัดการกับปัญหานี้หรือไม่? ขอบคุณ


1
เราสามารถลบล้าง BasicErrorController ของ Spring Boot ได้ ฉันเขียนบล็อกไว้ที่นี่: naturalprogrammer.com/blog/1685463/…
Sanjay

คำตอบ:


92

นี่คือสิ่งที่ฉันทำ:

ฉันอ่านข้อมูลพื้นฐานเกี่ยวกับตัวกรองที่นี่และฉันพบว่าฉันต้องสร้างตัวกรองที่กำหนดเองซึ่งจะเป็นอันดับแรกในห่วงโซ่ตัวกรองและจะมีการพยายามจับข้อยกเว้นรันไทม์ทั้งหมดที่อาจเกิดขึ้นที่นั่น จากนั้นฉันต้องสร้าง 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);
    }
}

จากนั้นฉันเพิ่มลงใน web.xml ก่อนหน้าไฟล์CorsFilter. และได้ผล!

<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>

คุณช่วยโพสต์คลาส ErrorResponse ได้ไหม
Shiva kumar

1
คลาส @Shivakumar ErrorResponse น่าจะเป็น DTO ธรรมดาที่มีคุณสมบัติโค้ด / ข้อความอย่างง่าย
ratijas

35

ผมอยากจะให้แก้ปัญหาบนพื้นฐานของคำตอบของ @kopelitsa ความแตกต่างที่สำคัญคือ:

  1. การนำการจัดการข้อยกเว้นของคอนโทรลเลอร์กลับมาใช้ใหม่โดยใช้HandlerExceptionResolver.
  2. การใช้ Java config ผ่าน XML config

ขั้นแรกคุณต้องตรวจสอบให้แน่ใจว่าคุณมีคลาสที่จัดการข้อยกเว้นที่เกิดขึ้นใน RestController / Controller ปกติ (คลาสที่มีคำอธิบายประกอบ@RestControllerAdviceหรือ@ControllerAdviceและวิธีการที่มีคำอธิบายประกอบ@ExceptionHandler) สิ่งนี้จัดการข้อยกเว้นของคุณที่เกิดขึ้นในคอนโทรลเลอร์ นี่คือตัวอย่างการใช้ RestControllerAdvice:

@RestControllerAdvice
public class ExceptionTranslator {

    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ErrorDTO processRuntimeException(RuntimeException e) {
        return createErrorDTO(HttpStatus.INTERNAL_SERVER_ERROR, "An internal server error occurred.", e);
    }

    private ErrorDTO createErrorDTO(HttpStatus status, String message, Exception e) {
        (...)
    }
}

หากต้องการใช้ลักษณะการทำงานนี้ซ้ำในห่วงโซ่ตัวกรอง Spring Security คุณต้องกำหนดตัวกรองและต่อเข้ากับการกำหนดค่าความปลอดภัยของคุณ ตัวกรองจำเป็นต้องเปลี่ยนเส้นทางข้อยกเว้นไปยังการจัดการข้อยกเว้นที่กำหนดไว้ข้างต้น นี่คือตัวอย่าง:

@Component
public class FilterChainExceptionHandler extends OncePerRequestFilter {

    private final Logger log = LoggerFactory.getLogger(getClass());

    @Autowired
    @Qualifier("handlerExceptionResolver")
    private HandlerExceptionResolver resolver;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        try {
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            log.error("Spring Security Filter Chain Exception:", e);
            resolver.resolveException(request, response, null, e);
        }
    }
}

จากนั้นตัวกรองที่สร้างขึ้นจะต้องถูกเพิ่มไปยัง SecurityConfiguration คุณต้องต่อมันเข้ากับโซ่ตั้งแต่เนิ่นๆเพราะจะไม่พบข้อยกเว้นของตัวกรองก่อนหน้าทั้งหมด ในกรณีของฉันมันสมเหตุสมผลที่จะเพิ่มก่อนหน้าไฟล์LogoutFilter. ดูห่วงโซ่กรองเริ่มต้นและการสั่งซื้อของในเอกสารอย่างเป็นทางการ นี่คือตัวอย่าง:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private FilterChainExceptionHandler filterChainExceptionHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .addFilterBefore(filterChainExceptionHandler, LogoutFilter.class)
            (...)
    }

}

ดูแนวโน้ม ... วิธีที่คุณจะปรับเปลี่ยนให้ข้อยกเว้นจับโยนโดยvalidators ? พวกเขายังทำงานก่อน Controller
Honza Zidek

FilterChainExceptionHandlerจะใช้ได้เฉพาะข้อยกเว้นรันไทม์เท่านั้น สิ่งที่ต้องทำสำหรับข้อยกเว้นที่ตรวจสอบแล้วเช่นMethodArgumentNotValidException , ServletRequestBindingExceptionฯลฯ
ไอย์เทล

1
ขอบคุณมากที่ทำให้วันของฉันฉันใช้เวลาส่วนใหญ่ในการจัดการ แต่ในตอนท้ายฉันเห็นสิ่งนี้และมันใช้งานได้ดี
Az.MaYo

19

ฉันเจอปัญหานี้ด้วยตัวเองและฉันได้ทำตามขั้นตอนด้านล่างนี้เพื่อใช้ซ้ำExceptionControllerที่มีคำอธิบายประกอบ@ControllerAdviseสำหรับExceptionsโยนในตัวกรองที่ลงทะเบียน

เห็นได้ชัดว่ามีหลายวิธีในการจัดการข้อยกเว้น แต่ในกรณีของฉันฉันต้องการให้ฉันจัดการข้อยกเว้นExceptionControllerเพราะฉันดื้อและเพราะฉันไม่ต้องการคัดลอก / วางรหัสเดียวกัน (เช่นฉันมีการประมวลผล / การบันทึก รหัสในExceptionController) ฉันต้องการคืนการJSONตอบสนองที่สวยงามเช่นเดียวกับข้อยกเว้นอื่น ๆ ที่ไม่ได้มาจากตัวกรอง

{
  "status": 400,
  "message": "some exception thrown when executing the request"
}

อย่างไรก็ตามฉันสามารถใช้ประโยชน์จากของฉันได้ExceptionHandlerและฉันต้องทำพิเศษเล็กน้อยดังแสดงด้านล่างในขั้นตอน

ขั้นตอน


  1. คุณมีตัวกรองแบบกำหนดเองที่อาจมีหรือไม่มีข้อยกเว้นก็ได้
  2. คุณมีตัวควบคุม Spring ที่จัดการข้อยกเว้นโดยใช้@ControllerAdviseเช่น MyExceptionController

โค้ดตัวอย่าง

//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 ตัวอย่างที่จัดการExceptionในกรณีปกติ (เช่นข้อยกเว้นที่มักจะไม่ถูกโยนในระดับตัวกรองซึ่งเป็นสิ่งที่เราต้องการใช้สำหรับข้อยกเว้นที่โยนในตัวกรอง)

//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
}

แบ่งปันวิธีแก้ปัญหากับผู้ที่ต้องการใช้ExceptionControllerสำหรับการExceptionsโยนในตัวกรอง


10
คุณสามารถแบ่งปันวิธีแก้ปัญหาของคุณเองซึ่งเป็นวิธีที่จะทำได้ :)
Raf

1
หากคุณต้องการหลีกเลี่ยงไม่ให้ Controller ต่อสายเข้ากับตัวกรองของคุณ (ซึ่งเป็นสิ่งที่ @ Bato-BairTsyrenov อ้างถึง) คุณสามารถแยกตรรกะที่คุณสร้าง ErrorDTO ลงใน@Componentคลาสของตัวเองได้อย่างง่ายดายและใช้ในตัวกรองและใน ตัวควบคุม
Rüdiger Schulz

1
ฉันไม่เห็นด้วยกับคุณโดยสิ้นเชิงเพราะการฉีดตัวควบคุมเฉพาะในตัวกรองของคุณไม่สะอาด
psv

ดังที่กล่าวมาanswerนี้เป็นหนึ่งในวิธี! ฉันไม่ได้อ้างว่าเป็นวิธีที่ดีที่สุด ขอบคุณสำหรับการแบ่งปันความกังวลของคุณ @psv ฉันแน่ใจว่าชุมชนจะซาบซึ้งกับการแก้ปัญหาที่คุณมีในใจ :)
Raf

12

ดังนั้นนี่คือสิ่งที่ฉันทำโดยอาศัยการรวมกันของคำตอบข้างต้น ... เรามีGlobalExceptionHandlerคำอธิบายประกอบอยู่แล้ว@ControllerAdviceและฉันก็ต้องการหาวิธีใช้รหัสนั้นซ้ำเพื่อจัดการข้อยกเว้นที่มาจากตัวกรอง

วิธีแก้ปัญหาที่ง่ายที่สุดที่ฉันพบคือปล่อยให้ตัวจัดการข้อยกเว้นอยู่คนเดียวและใช้ตัวควบคุมข้อผิดพลาดดังนี้:

@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โดยไม่มีการแก้ไข

สาเหตุใดที่เป็นความคิดที่ไม่ดี?


1
ขอบคุณตอนนี้การทดสอบโซลูชันนี้ แต่ในกรณีของฉันทำงานได้สมบูรณ์แบบ
Maciej

การเพิ่มหนึ่งครั้งที่สะอาดและเรียบง่ายสำหรับสปริงบูต 2.0+ ที่คุณควรเพิ่ม @Override public String getErrorPath() { return null; }
Fma

1
คุณสามารถใช้ javax.servlet.RequestDispatcher.ERROR_EXCEPTION แทน "javax.servlet.error.exception"
Marx

9

หากคุณต้องการวิธีทั่วไปคุณสามารถกำหนดหน้าข้อผิดพลาดใน 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.";
    }
}

แต่ใน API ที่เหลือการเปลี่ยนเส้นทางด้วยตำแหน่งไม่ใช่วิธีแก้ปัญหาที่ดี
jmattheis

@jmattheis ข้างต้นไม่ใช่การเปลี่ยนเส้นทาง
holmis83

จริงอยู่ฉันเห็นตำแหน่งและคิดว่ามันมีสิ่งที่ต้องทำกับตำแหน่ง http นี่คือสิ่งที่ฉันต้องการ (:
jmattheis

คุณสามารถเพิ่มการกำหนดค่า Java ที่เทียบเท่ากับ web.xml ได้หรือไม่ถ้ามีอยู่
k-den

1
@ k-den ไม่มีการกำหนดค่า Java ที่เทียบเท่าในข้อมูลจำเพาะปัจจุบันฉันคิดว่า แต่คุณสามารถผสม web.xml และ Java config
holmis83

5

นี่เป็นวิธีแก้ปัญหาของฉันโดยการแทนที่ Spring Boot / ตัวจัดการข้อผิดพลาดเริ่มต้น

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) ;
    }

}

มีผลต่อการกำหนดค่าอัตโนมัติหรือไม่
Samet Baskıcı

โปรดทราบว่า HandlerExceptionResolver ไม่จำเป็นต้องจัดการกับข้อยกเว้น ดังนั้นมันอาจจะผ่านเป็น HTTP 200 การใช้ response.setStatus (.. ) ก่อนที่จะโทรดูเหมือนจะปลอดภัย
ThomasRS

5

เพียงเพื่อเสริมคำตอบที่ดีอื่น ๆ ที่ให้มาเนื่องจากเมื่อเร็ว ๆ นี้ฉันต้องการส่วนประกอบการจัดการข้อผิดพลาด / ข้อยกเว้นเดียวในแอป SpringBoot ที่เรียบง่ายที่มีตัวกรองที่อาจทำให้เกิดข้อยกเว้นโดยมีข้อยกเว้นอื่น ๆ ที่อาจเกิดขึ้นจากวิธีการควบคุม

โชคดีที่ดูเหมือนว่าไม่มีอะไรที่จะป้องกันไม่ให้คุณรวมคำแนะนำตัวควบคุมของคุณเข้ากับการแทนที่ตัวจัดการข้อผิดพลาดเริ่มต้นของ Spring เพื่อให้มีน้ำหนักบรรทุกการตอบสนองที่สอดคล้องกันช่วยให้คุณแชร์ตรรกะตรวจสอบข้อยกเว้นจากตัวกรองดักจับข้อยกเว้นที่เกิดจากบริการที่เฉพาะเจาะจง

เช่น


@ControllerAdvice
@RestController
public class GlobalErrorHandler implements ErrorController {

  @ResponseStatus(HttpStatus.BAD_REQUEST)
  @ExceptionHandler(ValidationException.class)
  public Error handleValidationException(
      final ValidationException validationException) {
    return new Error("400", "Incorrect params"); // whatever
  }

  @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  @ExceptionHandler(Exception.class)
  public Error handleUnknownException(final Exception exception) {
    return new Error("500", "Unexpected error processing request");
  }

  @RequestMapping("/error")
  public ResponseEntity handleError(final HttpServletRequest request,
      final HttpServletResponse response) {

    Object exception = request.getAttribute("javax.servlet.error.exception");

    // TODO: Logic to inspect exception thrown from Filters...
    return ResponseEntity.badRequest().body(new Error(/* whatever */));
  }

  @Override
  public String getErrorPath() {
    return "/error";
  }

}

3

เมื่อคุณต้องการทดสอบสถานะของแอปพลิเคชันและในกรณีที่มีปัญหาส่งกลับข้อผิดพลาด HTTP ฉันขอแนะนำตัวกรอง ตัวกรองด้านล่างจัดการคำขอ HTTP ทั้งหมด โซลูชันที่สั้นที่สุดใน Spring Boot พร้อมตัวกรอง javax

ในการดำเนินการสามารถเงื่อนไขต่างๆ ในกรณีของฉัน 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() {}
}

2

หลังจากอ่านวิธีต่างๆที่แนะนำในคำตอบข้างต้นฉันตัดสินใจจัดการข้อยกเว้นการตรวจสอบสิทธิ์โดยใช้ตัวกรองที่กำหนดเอง ฉันสามารถจัดการสถานะการตอบกลับและรหัสโดยใช้คลาสตอบสนองข้อผิดพลาดโดยใช้วิธีการต่อไปนี้

ฉันสร้างตัวกรองแบบกำหนดเองและแก้ไขการกำหนดค่าความปลอดภัยของฉันโดยใช้เมธอด addFilterAfter และเพิ่มหลังจากคลาส CorsFilter

@Component
public class AuthFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    //Cast the servlet request and response to HttpServletRequest and HttpServletResponse
    HttpServletResponse httpServletResponse = (HttpServletResponse) response;
    HttpServletRequest httpServletRequest = (HttpServletRequest) request;

    // Grab the exception from the request attribute
    Exception exception = (Exception) request.getAttribute("javax.servlet.error.exception");
    //Set response content type to application/json
    httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);

    //check if exception is not null and determine the instance of the exception to further manipulate the status codes and messages of your exception
    if(exception!=null && exception instanceof AuthorizationParameterNotFoundException){
        ErrorResponse errorResponse = new ErrorResponse(exception.getMessage(),"Authetication Failed!");
        httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        PrintWriter writer = httpServletResponse.getWriter();
        writer.write(convertObjectToJson(errorResponse));
        writer.flush();
        return;
    }
    // If exception instance cannot be determined, then throw a nice exception and desired response code.
    else if(exception!=null){
            ErrorResponse errorResponse = new ErrorResponse(exception.getMessage(),"Authetication Failed!");
            PrintWriter writer = httpServletResponse.getWriter();
            writer.write(convertObjectToJson(errorResponse));
            writer.flush();
            return;
        }
        else {
        // proceed with the initial request if no exception is thrown.
            chain.doFilter(httpServletRequest,httpServletResponse);
        }
    }

public String convertObjectToJson(Object object) throws JsonProcessingException {
    if (object == null) {
        return null;
    }
    ObjectMapper mapper = new ObjectMapper();
    return mapper.writeValueAsString(object);
}
}

คลาส SecurityConfig

    @Configuration
    public class JwtSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    AuthFilter authenticationFilter;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterAfter(authenticationFilter, CorsFilter.class).csrf().disable()
                .cors(); //........
        return http;
     }
   }

คลาส ErrorResponse

public class ErrorResponse  {
private final String message;
private final String description;

public ErrorResponse(String description, String message) {
    this.message = message;
    this.description = description;
}

public String getMessage() {
    return message;
}

public String getDescription() {
    return description;
}}

0

คุณสามารถใช้วิธีการต่อไปนี้ในบล็อกจับ:

response.sendError(HttpStatus.UNAUTHORIZED.value(), "Invalid token")

สังเกตว่าคุณสามารถใช้รหัส HttpStatus และข้อความที่กำหนดเองได้


-1

มันแปลกเพราะ @ControllerAdvice ควรใช้งานได้คุณจับ Exception ที่ถูกต้องหรือไม่?

@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 และส่งข้อผิดพลาด 500 รายการเช่นนี้

@ExceptionHandler(DataAccessException.class)
@ResponseBody
public String handleDataException(DataAccessException ex, HttpServletResponse response) {
    response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
    //Json return
}

การจัดการข้อยกเว้นใน CorsFilter ใช้งานได้ แต่ไม่ค่อยสะอาดนัก ในความเป็นจริงสิ่งที่ฉันต้องการจริงๆคือการจัดการข้อยกเว้นสำหรับตัวกรองทั้งหมด
kopelitsa

35
ยกเว้นโยนจากFilterไม่อาจจะจับโดยเพราะอาจไม่ถึง@ControllerAdvice DispatcherServlet
Thanh Nguyen Van

-1

คุณไม่จำเป็นต้องสร้างตัวกรองแบบกำหนดเองสำหรับสิ่งนี้ เราแก้ไขปัญหานี้โดยการสร้างข้อยกเว้นแบบกำหนดเองที่ขยาย ServletException (ซึ่งถูกส่งมาจากเมธอด doFilter ที่แสดงในการประกาศ) จากนั้นสิ่งเหล่านี้จะถูกจับและจัดการโดยตัวจัดการข้อผิดพลาดทั่วโลกของเรา

แก้ไข: ไวยากรณ์


คุณช่วยแบ่งปันข้อมูลโค้ดของตัวจัดการข้อผิดพลาดส่วนกลางของคุณได้ไหม
Neeraj Vernekar

มันไม่ได้ผลสำหรับฉัน ฉันสร้างข้อยกเว้นแบบกำหนดเองซึ่งขยาย ServletException เพิ่มการสนับสนุนสำหรับข้อยกเว้นนี้ใน ExceptionHandler แต่ไม่ได้ถูกดักจับที่นั่น
Marx
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.