如何在Spring Boot中处理DeferredResult中的异常?

时间:2017-07-20 12:12:34

标签: java spring spring-boot nonblocking

我有一个休息方法:

@RequestMapping(value = "wash/washHistory", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
    @ResponseBody
    public DeferredResult<String> getWashHistory(@RequestParam(value = "sid", required = true, defaultValue = "") String sid,
            HttpServletResponse response, HttpServletRequest request,
            @RequestParam(value = "sort", defaultValue = "") String sortType,
            @RequestParam(value = "order", defaultValue = "") String order,
            @RequestParam(value = "limit", defaultValue = "") String limit,
            @RequestParam(value = "offset", defaultValue = "") String offset) {

        System.out.println("Thread: "+Thread.currentThread());
        final Integer managerId = checkSession(sid);      
        DeferredResult<String> defResult = new DeferredResult<>();
        new Thread(() -> {
                final String result = washController.getWashHistory( managerId, order, sortType, limit, offset);
                defResult.setResult(result);            
        }).start();
    return defResult;
    }

在“getWashHistory”中我抛出以下自定义异常:

throw new InvalidUserInputException("Wrong offset", this.getClass().getSimpleName(), "getWashHist", params);

为了处理这个异常我正在使用以下类:

@ControllerAdvice
@EnableWebMvc
public class GlobalExceptionHandler {
 @ExceptionHandler(value = InvalidUserInputException.class)
    public ResponseEntity<String> invalidUserInputExc(InvalidUserInputException e) {
          logger.log("GMoika", e.error().getClassName(), e.error().getMethodName(), e.error().getParams(), e.error().getCause());
        return ResponseEntity.
                status(HttpStatus.BAD_REQUEST).
                body(e.error().getErrorCode());  
    }
}

只要我没有使用DeferredResult,它就可以正常工作,但是当我想使用非阻塞方式时,会发生超时异常。 我找到了一种解决方法:

defResult.onTimeout(new Runnable() {
        @Override
        public void run() {
            defResult.setErrorResult("Explanation goes here.");
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); //or SC_NO_CONTENT
        }
    });

但它不是我正在寻找的东西,因为我用特定的构造函数抛出我自己的异常来写入引发此异常的类内部的原因。 在GlobalExceptionHandler类中是否还有其他可能的方法来处理DeferredResult中的异常?

3 个答案:

答案 0 :(得分:4)

DeferredResult方法setErrorResult可以采用异常并根据documentation

  

该值可能是Exception或Throwable,在这种情况下它将是   像处理程序引发异常一样处理。

答案 1 :(得分:3)

在大多数情况下,@ jny的决定是正确的,但对我来说,我找到了另一种方式 在我的休息控制器中,我添加了以下代码:

new Thread(() -> {          
            Thread.currentThread().setUncaughtExceptionHandler(new SeparateThreadsExceptionHandler(defResult));
            final String result = washController.getWashHistory(managerId, order, sortType, limit, offset);
            defResult.setResult(result);
        }).start();

还有一个SeparateThreadsExceptionHandler类:

public class SeparateThreadsExceptionHandler implements Thread.UncaughtExceptionHandler{
    private DeferredResult<String> dr;
    public SeparateThreadsExceptionHandler(DeferredResult<String> dr){
        this.dr = dr;
    }
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        if(e instanceof InvalidUserInputException){
           InvalidUserInputException throwableException =  (InvalidUserInputException)e;
            dr.setResult(throwableException.error().getErrorCode());
        } else {
            dr.setResult(UnknownException.UNKNOWN_ERROR_CODE);
        }
    }

}

当我抛出自定义异常时,我可以为DeferredResult设置一些字符串错误消息。在我的例子中,它是前端的错误代码。

答案 2 :(得分:0)

  

在我的GlobalExceptionHandler类中是否还有其他可能的方法来处理DeferredResult中的异常

我使用了@jny指出的2018-12-07T08:45:12.931589Z 22 Query SELECT nav FROM main_nav WHERE 'main/index' LIKE CONCAT(url, '%') OR '/main/index/' LIKE CONCAT(url, '%') OR '/main/index' LIKE CONCAT(url, '%') OR 'main/index' LIKE CONCAT(url, '%') ORDER BY LENGTH(url) DESC方法

这是我的控制器方法的主体:

setErrorResult

在这种情况下,新线程中引发的异常将传递给全局@ExceptionHandler