복붙노트

[SPRING] HTML과 JSON 요청에 대해 Spring MVC에서 예외를 다르게 처리하는 방법

SPRING

HTML과 JSON 요청에 대해 Spring MVC에서 예외를 다르게 처리하는 방법

Spring 4.0.3에서 다음 예외 핸들러를 사용하여 예외를 가로 채고 사용자에게 사용자 정의 오류 페이지를 표시합니다.

@ControllerAdvice
public class ExceptionHandlerController
{
    @ExceptionHandler(value = Exception.class)
    public ModelAndView handleError(HttpServletRequest request, Exception e)
    {
        ModelAndView mav = new ModelAndView("/errors/500"));
        mav.addObject("exception", e);
        return mav;
    }
}

하지만 지금은 JSON 요청에 대한 다른 처리를 원합니다. 따라서 예외가 발생했을 때 이러한 종류의 요청에 대한 JSON 오류 응답을받습니다. 현재 위의 코드는 JSON 요청 (Accept : application / json 헤더 사용)에 의해 트리거되고 JavaScript 클라이언트는 HTML 응답을 좋아하지 않습니다.

HTML 및 JSON 요청에 대해 예외를 다르게 처리하려면 어떻게해야합니까?

해결법

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

    1.ControllerAdvice 어노테이션은 basePackage라는 요소 / 속성을 가지고 있는데,이 요소 / 컨트롤러는 어떤 패키지를 컨트롤러에 스캔하고 조언을 적용할지 결정하기 위해 설정할 수있다. 따라서, 정상적인 요청을 처리하는 컨트롤러와 AJAX 요청을 처리하는 컨트롤러를 서로 다른 패키지로 분리 한 다음 적합한 ControllerAdvice 주석이있는 2 개의 예외 처리 컨트롤러를 작성해야합니다. 예 :

    ControllerAdvice 어노테이션은 basePackage라는 요소 / 속성을 가지고 있는데,이 요소 / 컨트롤러는 어떤 패키지를 컨트롤러에 스캔하고 조언을 적용할지 결정하기 위해 설정할 수있다. 따라서, 정상적인 요청을 처리하는 컨트롤러와 AJAX 요청을 처리하는 컨트롤러를 서로 다른 패키지로 분리 한 다음 적합한 ControllerAdvice 주석이있는 2 개의 예외 처리 컨트롤러를 작성해야합니다. 예 :

    @ControllerAdvice("com.acme.webapp.ajaxcontrollers")
    public class AjaxExceptionHandlingController {
    ...
    @ControllerAdvice("com.acme.webapp.controllers")
    public class ExceptionHandlingController {
    
  2. ==============================

    2.이것을 수행하는 가장 좋은 방법은 (특히 서블릿 3에서) 오류 페이지를 컨테이너에 등록하고이를 사용하여 Spring @Controller를 호출하는 것입니다. 그런 식으로 표준 Spring MVC 방식 (예 : @RequestMapping with produce = ...을 사용)에서 다양한 응답 유형을 처리 할 수 ​​있습니다.

    이것을 수행하는 가장 좋은 방법은 (특히 서블릿 3에서) 오류 페이지를 컨테이너에 등록하고이를 사용하여 Spring @Controller를 호출하는 것입니다. 그런 식으로 표준 Spring MVC 방식 (예 : @RequestMapping with produce = ...을 사용)에서 다양한 응답 유형을 처리 할 수 ​​있습니다.

    나는 당신의 다른 질문에서 당신이 봄 부팅을 사용하고 있음을 알 수 있습니다. 스냅 샷 (1.1 또는 그 이상)으로 업그레이드하면이 동작을 즉시 사용할 수 있습니다 (BasicErrorController 참조). 그것을 오버라이드하려면 / error 경로를 자신의 @Controller에 매핑하면됩니다.

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

    3.HttpServletRequest를 사용하면 요청 "Accept"헤더를 가져올 수 있습니다. 그런 다음이를 기반으로 예외를 처리 할 수 ​​있습니다.

    HttpServletRequest를 사용하면 요청 "Accept"헤더를 가져올 수 있습니다. 그런 다음이를 기반으로 예외를 처리 할 수 ​​있습니다.

    같은 것 :

    String header = request.getHeader("Accept");
    if(header != null && header.equals("application/json")) {
        // Process JSON exception
    } else {
        ModelAndView mav = new ModelAndView("/errors/500"));
        mav.addObject("exception", e);
        return mav;
    }
    
  4. ==============================

    4.이후로 나는이 문제에 대한 해결책을 찾지 못했기 때문에 형식을 결정하기 위해 요청의 수락 헤더를 수동으로 검사하는 코드를 작성했습니다. 그런 다음 사용자가 로그인되어 있는지 확인하고 짧은 스택 오류 메시지 인 경우 전체 스택 추적을 보냅니다.

    이후로 나는이 문제에 대한 해결책을 찾지 못했기 때문에 형식을 결정하기 위해 요청의 수락 헤더를 수동으로 검사하는 코드를 작성했습니다. 그런 다음 사용자가 로그인되어 있는지 확인하고 짧은 스택 오류 메시지 인 경우 전체 스택 추적을 보냅니다.

    ResponseEntity를 사용하여 여기에서 JSON 또는 HTML을 모두 반환 할 수 있습니다. 암호:

    @ExceptionHandler(Exception.class)
    public ResponseEntity<?> handleExceptions(Exception ex, HttpServletRequest request) throws Exception {
    
        final HttpHeaders headers = new HttpHeaders();
        Object answer; // String if HTML, any object if JSON
        if(jsonHasPriority(request.getHeader("accept"))) {
            logger.info("Returning exception to client as json object");
            headers.setContentType(MediaType.APPLICATION_JSON);
            answer = errorJson(ex, isUserLoggedIn());
        } else {
            logger.info("Returning exception to client as html page");
            headers.setContentType(MediaType.TEXT_HTML);
            answer = errorHtml(ex, isUserLoggedIn());
        }
        final HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
        return new ResponseEntity<>(answer, headers, status);
    }
    
    private String errorHtml(Exception e, boolean isUserLoggedIn) {
        String error = // html code with exception information here
        return error;
    }
    
    private Object errorJson(Exception e, boolean isUserLoggedIn) {
        // return error wrapper object which will be converted to json
        return null;
    }
    
    /**
     * @param acceptString - HTTP accept header field, format according to HTTP spec:
     *      "mime1;quality1,mime2;quality2,mime3,mime4,..." (quality is optional)
     * @return true only if json is the MIME type with highest quality of all specified MIME types.
     */
    private boolean jsonHasPriority(String acceptString) {
        if (acceptString != null) {
            final String[] mimes = acceptString.split(",");
            Arrays.sort(mimes, new MimeQualityComparator());
            final String firstMime = mimes[0].split(";")[0];
            return firstMime.equals("application/json");
        }
        return false;
    }
    
    private static class MimeQualityComparator implements Comparator<String> {
        @Override
        public int compare(String mime1, String mime2) {
            final double m1Quality = getQualityofMime(mime1);
            final double m2Quality = getQualityofMime(mime2);
            return Double.compare(m1Quality, m2Quality) * -1;
        }
    }
    
    /**
     * @param mimeAndQuality - "mime;quality" pair from the accept header of a HTTP request,
     *      according to HTTP spec (missing mimeQuality means quality = 1).
     * @return quality of this pair according to HTTP spec.
     */
    private static Double getQualityofMime(String mimeAndQuality) {
        //split off quality factor
        final String[] mime = mimeAndQuality.split(";");
        if (mime.length <= 1) {
            return 1.0;
        } else {
            final String quality = mime[1].split("=")[1];
            return Double.parseDouble(quality);
        }
    }
    
  5. ==============================

    5.controlleradvice 주석은 봄 4부터 설정할 수있는 몇 가지 속성을 가지고 있습니다. 다른 규칙을 적용하는 여러 컨트롤러 조언을 정의 할 수 있습니다.

    controlleradvice 주석은 봄 4부터 설정할 수있는 몇 가지 속성을 가지고 있습니다. 다른 규칙을 적용하는 여러 컨트롤러 조언을 정의 할 수 있습니다.

    하나의 속성은 "주석입니다. 아마도 json 요청 매핑에서 특정 주석을 사용하거나 다른 속성을 더 유용하게 사용할 수 있습니까?

  6. ==============================

    6.@ControllerAdvice 사용 예외 처리기가 필드 오류를 포함하는 DTO를 보내 게하십시오.

    @ControllerAdvice 사용 예외 처리기가 필드 오류를 포함하는 DTO를 보내 게하십시오.

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ValidationErrorDTO processValidationError(MethodArgumentNotValidException ex) {
        BindingResult result = ex.getBindingResult();
        List<FieldError> fieldErrors = result.getFieldErrors();
    
        return processFieldErrors(fieldErrors);
    }
    

    이 코드는이 웹 사이트에 있습니다 : http : //www.petrikainulainen.net/programming/spring-framework/spring-from-the-trenches-adding-validation-to-a-rest-api/ 더 많은 정보를 찾으십시오.

  7. ==============================

    7.트릭은 REST 컨트롤러에 두 개의 매핑이 있고 그 중 하나가 "text / html"을 지정하고 유효한 HTML 소스를 반환하는 것입니다. 아래의 예제는 Spring 부트 2.0에서 테스트되었지만 "error.html"이라는 이름의 별도의 템플릿이 있다고 가정합니다.

    트릭은 REST 컨트롤러에 두 개의 매핑이 있고 그 중 하나가 "text / html"을 지정하고 유효한 HTML 소스를 반환하는 것입니다. 아래의 예제는 Spring 부트 2.0에서 테스트되었지만 "error.html"이라는 이름의 별도의 템플릿이 있다고 가정합니다.

    @RestController
    public class CustomErrorController implements ErrorController {
    
        @Autowired
        private ErrorAttributes errorAttributes;
    
        private Map<String,Object> getErrorAttributes( HttpServletRequest request ) {
            WebRequest webRequest = new ServletWebRequest(request);
            boolean includeStacktrace = false;
            return errorAttributes.getErrorAttributes(webRequest,includeStacktrace);
        }
    
        @GetMapping(value="/error", produces="text/html")
        ModelAndView errorHtml(HttpServletRequest request) {
            return new ModelAndView("error.html",getErrorAttributes(request));
        }
    
        @GetMapping(value="/error")
        Map<String,Object> error(HttpServletRequest request) {
            return getErrorAttributes(request);
        }
    
        @Override public String getErrorPath() { return "/error"; }
    
    }
    
  8. from https://stackoverflow.com/questions/23582534/how-to-handle-exceptions-in-spring-mvc-differently-for-html-and-json-requests by cc-by-sa and MIT license