【问题标题】:Why ExceptionHandlerExceptionResolver makes redirect instead of simple response?为什么 ExceptionHandlerExceptionResolver 进行重定向而不是简单响应?
【发布时间】:2020-06-05 20:02:46
【问题描述】:

我已经编写了 ExceptionHandlerExceptionResolver 的一些扩展,它拦截了它应该拦截的所有异常,但是它不是只返回错误消息和 HTTP 状态代码,而是通过它自己的基于用户请求的 URL 构建的 URL 进行非常奇怪的重定向。例如:

user's url -> .../myModule/api/myEntity/123 (it's an id)
resolver's redirect url -> .../myModule/api/myEntity/myEntity/123 

服务器没有这样的资源,显然它会以 404 响应。

问题是:为什么要进行重定向以及如何将其配置为仅返回消息和状态码?

我的解析器:

public class BusinessLayerExceptionHandler extends ExceptionHandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        ModelAndView wrappedResponse = new ModelAndView();
        wrappedResponse.addObject("errorMessage", ex.getMessage());
        wrappedResponse.setStatus(HttpStatus.BAD_REQUEST);
        return wrappedResponse;
    }
}

我猜ModelAndView 的用法假定重定向。至少这是我在 DispatcherServlet 中找到的方法描述。

...
     * @return a corresponding ModelAndView to forward to
     * @throws Exception if no error ModelAndView found
     */
    protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
            Object handler, Exception ex) throws Exception {
...

如果是这样,如何让它只返回错误消息和 HTTP 状态码?

【问题讨论】:

    标签: java spring spring-mvc


    【解决方案1】:

    您可以通过创建自定义 View 来仅返回错误消息和 HTTP 状态代码。

    public class YourCustomView implements View {
    
        private final String errorMessage;
    
        public YourCustomView(String errorMessage) {
            this.errorMessage = errorMessage;
        }
    
        @Override
        public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
                throws Exception {
            response.setContentType("text/plain;charset=UTF-8");
            try (PrintWriter pw = response.getWriter()) {
                pw.write(errorMessage);
            }
        }
    
    }
    

    您需要将自定义的View 对象放入HandlerExceptionResolver#resolveException 中的ModelAndView 对象中。

    public class BusinessLayerExceptionHandler implements HandlerExceptionResolver {
    
        @Override
        public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
                Exception ex) {
            ModelAndView wrappedResponse = new ModelAndView();
            wrappedResponse.setStatus(HttpStatus.BAD_REQUEST);
            wrappedResponse.setView(new YourCustomView(ex.getMessage()));
            return wrappedResponse;
        }
    }
    


    为什么要重定向

    似乎 Spring 将视图名称识别为 defaultViewName 并转发给它(通过调用 RequestDispatcher#forward)。
    DispatcherServlet#processHandlerException 中,当ModelAndView 没有View 对象时,ModelAndView 返回的ModelAndView 的视图名称设置为DispatcherServlet#processHandlerExceptiondefaultViewName 来自 DispatcherServlet#getDefaultViewName,它将 HTTP 请求转换为视图名称。


    另一种解决方案

    我认为您可以改用@ControllerAdvice@ExceptionHandler。它还可以处理控制器抛出的异常。

    @ControllerAdvice
    public class YourControllerAdvice {
    
        @ExceptionHandler
        public ResponseEntity<Map<String, String>> handleBusinessLayerException(
                Exception exception) {
            Map<String, String> body = Map.of("errorMessage", exception.getMessage());
            return ResponseEntity.badRequest().body(body);
        }
    
    }
    
    

    另见

    Spring Web MVC document about HandlerExceptionResolver
    Spring Web MVC document about ControllerAdvice

    【讨论】:

      猜你喜欢
      • 2019-06-04
      • 1970-01-01
      • 1970-01-01
      • 2020-12-28
      • 2018-02-12
      • 2022-12-07
      • 2012-06-12
      • 1970-01-01
      • 2011-07-14
      相关资源
      最近更新 更多