본문 바로가기

spring

AJAX 호출시 Exception 처리방법

Spring 프레임워크로 프로젝트를 진행하다 AJAX로 호출했을 때 Exception을 어떻게 처리해야 될까 고민하다 나온 방법이다. 이렇게 사용해도 되는지 확신은 없지만, 더 나은 방법은 아직 찾지 못했다.

Spring MVC를 사용하면 대부분 예외처리 전략을 SimpleMappingExceptionResolver을 이용해서 처리한다. 예외가 발생하면 특정페이지로 이동시켜주는 역할을 하는데 설정시 아래와 같이 statusCodes라는 값을 정의해줄 수가 있고, 이렇게 정의하면 Mapping에 해당하는 값마다 HTTP 상태값을 줄수가 있다.

spring 설정

<bean id="exceptionResolver" class="com.tistory.aircook.common.resolver.ExceptionResolver"

    p:exceptionMappings-ref="exceptionMappings" p:exceptionAttribute="exceptionMessage"

    p:statusCodes-ref="statusCodes" />

<util:properties id="exceptionMappings">

    <prop key=" com.tistory.aircook.common.exception.AccessDeniedException">/exception/accessDenied</prop>

    <prop key=" com.tistory.aircook.common.exception.AuthenticationException">/exception/authentication</prop>

    <prop key="java.lang.Exception">/exception/exception</prop>

</util:properties>

<util:properties id="statusCodes">

    <prop key="/exception/accessDenied">401</prop>

    <prop key="/exception/authentication">403</prop>

    <prop key="/exception/exception">500</prop>

</util:properties>


AJAX호출일때만 HTTP 상태값을 변경하고 싶어서 SimpleMappingExceptionResolver을 상속받아  아래와 같이 doResolveException 메소드를 오버라이드했다.

exceptionResolver

@Override

protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

    // Expose ModelAndView for chosen error view.

    String viewName = determineViewName(ex, request);

    if (viewName != null) {

        if (logger.isDebugEnabled()) {

            logger.debug("AJAX 호출인지? : " + request.getHeader("AJAX"));

        }

        // ajax 호출인 경우에만 HTTP status code값을 변경시킨다.

        if (request.getHeader("AJAX") != null && request.getHeader("AJAX").equals("true")) {

            // Apply HTTP status code for error views, if specified.

            // Only apply it if we're processing a top-level request.

            Integer statusCode = determineStatusCode(request, viewName);

                if (statusCode != null) {

                    applyStatusCodeIfPossible(request, response, statusCode);

                }

        }

            return getModelAndView(viewName, ex, request);

        } else {

                return null;

        }
 
}


이제 아래와 같이 AJAX호출시 requestheader값에 내가 정의한 값을 같이 보내주면 된다. 아래는 jQuery를 이용한 AJAX호출이다.

html

//ajax 기본설정

$.ajaxSetup({

    beforeSend: function(xhr) {

        xhr.setRequestHeader("AJAX", true);

    },

    error: function(xhr, status, err) {

        if(xhr.status == 401){

            location.href = "<c:url value='/exception/accessDenied.do'/>";

        }else if(xhr.status == 403){

            location.href = "<c:url value='/exception/authentication.do'/>";

        }else{

            alert("예외가 발생했습니다. 관리자에게 문의하세요.");

        }

    }

});

 

이렇게 정의하면 일반적인 호출일때는 예외가 발생하면 SimpleMappingExceptionResolver에 의해 해당하는 페이지로 이동할 것이며, AJAX호출일때는 상태값에 따라 error일때의 상태값에 따라 자바스크립트에 의해 특정페이지로 이동할것이다. 예외에 대한 HTTP 상태값은 최대한 비슷한것으로 골라서 처리했다.

HttpServletResponse.SC_UNAUTHORIZED = 401

HttpServletResponse.SC_FORBIDDEN = 403

HttpServletResponse.SC_INTERNAL_SERVER_ERROR = 500

AccessDeniedException, AuthenticationException 클래스는 직접정의했으며 Interceptor에서도 아래와 같이 정의한 예외를 발생시키는 코드를 사용하였다.

interceptor

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

    if (logger.isDebugEnabled()) {

        logger.debug("AccessInterceptor.preHandle!");

        logger.debug("request.getHeader(\"User-Agent\") : " + request.getHeader("User-Agent"));

        logger.debug("request.getRequestURI() : " + request.getRequestURI());

    }

    //세션에서 정보를 가져온다.

    String sessionUserNo = (String) WebUtils.getSessionAttribute(request, "sessionUserNo");

        if (sessionUserNo == null) {

            if (logger.isDebugEnabled()) {

                logger.debug("세션정보가 없습니다.");

            }

            //ModelAndView mv = new ModelAndView("common/close");

            //throw new ModelAndViewDefiningException(mv);

            throw new AccessDeniedException("세션정보가 없습니다.");         

        }

        //세션에 사용자정보가 존재하면

        else{

        }

        return true;

 }