Repair HandlerMaping method execution if an exception occurs cannot be obtained in the interceptor.

Comment From: sdeleuze

Hi @yange, thanks for creating this updated PR. Could you please describe more in detail your use case ?

Comment From: ghost

@sdeleuze ,Hello, my English is weak, but I need in the actual project after the interceptor according to intercept to the abnormal record operation log.The interceptor class code below:

 @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response, Object arg2, Exception arg3)
            throws Exception {
        if (arg2 instanceof HandlerMethod) {
            HandlerMethod method = (HandlerMethod) arg2;
            AdminLog methodAnnotation = method.getMethodAnnotation(AdminLog.class);
            if (methodAnnotation != null) {
                if (RequestThread.getCurrentUser() != null) {
                    String action = methodAnnotation.action();
                    String module = methodAnnotation.module();
//                    if (RequestThread.getException() != null) {
                    if(arg3!=null){
//                        this.adminLogService.addLog("[failed]" + action + ":" + RequestThread.getException().getMessage(), module);
                        this.adminLogService.addLog("[failed]" + action + ":" + arg3.getMessage(), module);
                    } else {
                        this.adminLogService.addLog("[success]" + action, module);
                    }
                }
            }
        }
    }

Below is part of the code of the DispatcherServlet used to invoke the method of the interceptor:

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
            throws Exception {

        if (getInterceptors() == null) {
            return;
        }
        for (int i = this.interceptorIndex; i >= 0; i--) {
            HandlerInterceptor interceptor = getInterceptors()[i];
            try {
                interceptor.afterCompletion(request, response, this.handler, ex);
            }
            catch (Throwable ex2) {
                logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
            }
        }
    }

Don't know if such instructions are too vague, but I feel there is exception parameters should be passed to the triggerAfterCompletion, otherwise the interceptor will not be able to receive this exception.

Comment From: rstoyanchev

The relevant part of the DispatcherServlet is in processDispatchResult where it can invoke triggerAfterCompletion with a null parameter if the exception was handled with a HandlerExceptionResolver. Is the exception being handled with such a resolver in your case?

Comment From: ghost

Hi,@rstoyanchev , @sdeleuze, Yes, The Controller class has @RequestMapping annotationed method and has a @ExceptionHandler annotationed method is used to handle exceptions in parent class, but still need a interceptor class used to record the success or failure of the operation. In this case, so if an exception occurs, I hope I can in the interceptor can know abnormal happened. My English is very sorry.

Comment From: ghost

AdminBaseController

package com.ssdd.manager.web;

import com.sdyc.utils.json.JsonObjectUtils;
import com.ssdd.common.RequestThread;
import com.ssdd.manager.CurrentAdminUser;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public abstract class AdminBaseController
{

    protected Log log = LogFactory.getLog(this.getClass());


    /**
     * 异常页面控制
     *
     * @param runtimeException
     * @return
     */
    @ExceptionHandler(RuntimeException.class)
    public ModelAndView runtimeExceptionHandler(HttpServletRequest request,
                                                HttpServletResponse response, RuntimeException runtimeException)
    {


//This is not elegant way, abnormal storage to the ThreadLocal
        RequestThread.setException(runtimeException);
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("success", false);
        if (runtimeException instanceof AuthenticationException || runtimeException instanceof AuthorizationException)
        {

            map.put("msg", "对不起,您没有此操作的权限,请与帐号管理员联系");

        } else
        {
            map.put("msg", runtimeException.getMessage());
            map.put("trace", ExceptionUtils.getFullStackTrace(runtimeException));
            log.error("后台出现错误", runtimeException.getCause());
        }

        if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With")))
        { // EXTJS
            try
            {
                response.getWriter().println(JsonObjectUtils.beanToJson(map));
            } catch (IOException e)
            {
                e.printStackTrace();
            }
            return null;
        } else
        {
            return new ModelAndView("/admin/error", map);
        }
    }

    /**
     * 判断是不是超级用户
     *
     * @return
     */
    public boolean currentAdminUserIsSuperman()
    {
        CurrentAdminUser o = RequestThread.getCurrentUser(CurrentAdminUser.class);
        return o.isSuperman();
    }
}

AdminLogInterceptor


package com.ssdd.manager.web;

import com.ssdd.common.RequestThread;
import com.ssdd.manager.AdminLog;
import com.ssdd.manager.service.AdminLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 管理日志记录拦截器
 *
 * @author Administrator
 */
public class AdminLogInterceptor implements HandlerInterceptor {

    @Autowired
    AdminLogService adminLogService;


    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response, Object arg2, Exception arg3)
            throws Exception {
        if (arg2 instanceof HandlerMethod) {
            HandlerMethod method = (HandlerMethod) arg2;
            AdminLog methodAnnotation = method.getMethodAnnotation(AdminLog.class);
            if (methodAnnotation != null) {
                if (RequestThread.getCurrentUser() != null) {
                    String action = methodAnnotation.action();
                    String module = methodAnnotation.module();
//                    if (RequestThread.getException() != null) {
                    if(arg3!=null){
//                        this.adminLogService.addLog("[failed]" + action + ":" + RequestThread.getException().getMessage(), module);
                        this.adminLogService.addLog("[failed]" + action + ":" + arg3.getMessage(), module);
                    } else {
                        this.adminLogService.addLog("[success]" + action, module);
                    }
                }
            }
        }
    }


    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
                           Object arg2, ModelAndView arg3) throws Exception {

    }

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object arg2) throws Exception {


        return true;
    }

}

The above is my code, you can reference.If really need to pass a null here, please let me know.

Comment From: pivotal-issuemaster

@ghost Please sign the Contributor License Agreement!

Click here to manually synchronize the status of this Pull Request.

See the FAQ for frequently asked questions.

Comment From: rstoyanchev

Closing as the proposed change would also change the contract.