如何在混合MVC和REST Spring应用程序中实现异常处理?

问题描述 投票:0回答:1

Spring Boot 3.2.x / Spring Web 6.1.x

在同时具有 MVC 控制器(通过 JSP 返回 HTML 页面)和 REST 控制器的应用程序中,如何实现全局异常处理,以便 MVC 控制器方法导致错误page,而 REST 端点返回非页面响应(400、404) 、500 等,带有文本或 JSON 响应正文)?

我实现了一个

@ControllerAdvice
,可以使用特定的
@ExceptionHandler
@ResponseStatus
注释来处理各种异常。这些方法都返回一个页面(例如,
"forward:/error"
),因为它们应该用于 MVC 请求。

这是该课程的一部分:

@ControllerAdvice
public class GlobalDefaultExceptionHandler {

    //...other methods...

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public String defaultInternalErrorHandler(Model model, Exception e, HttpServletRequest req) {
        UUID errorID = UUID.randomUUID();
        log.error(errorID + " | Unhandled exception processing request to " + req.getRequestURI(), e);

        model.addAttribute("exception", e);
        model.addAttribute("errorID", errorID);
        return "forward:/error";
    }

}

如果其他方法都没有处理抛出的异常,则这是“后备”处理程序方法。这对于返回页面 (HTML) 的所有 MVC 控制器方法都非常有用。

但是,我不希望 REST 控制器端点返回 HTML 内容,它们应该返回(在我的例子中为 JSON)具有适当 HTTP 响应状态的内容(例如,无效请求为 400,服务器错误为 500 等)。

我尝试添加另一个与此类似的

@RestControllerAdvice
类:

@RestControllerAdvice
@Slf4j
public class GlobalRestExceptionHandler {

    @ExceptionHandler(ServletRequestBindingException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public String handle(ServletRequestBindingException e, HttpServletRequest req) {
        defaultInternalErrorHandler(e, req);
        return "Invalid Request: " + e.getMessage();
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public String defaultInternalErrorHandler(Exception e, HttpServletRequest req) {
        UUID errorID = UUID.randomUUID();
        log.error(errorID + " | Unhandled exception processing API request to " + req.getRequestURI(), e);
        return "Server error: " + e;
    }
}

不幸的是,那个类似乎没有被使用。来自 REST 端点的任何异常都会流经

@ControllerAdvice
默认方法并返回错误 page

如何才能两全其美:MVC 请求转到错误页面,REST 端点返回 REST 友好 (JSON) 内容?

spring spring-boot spring-mvc spring-restcontroller
1个回答
0
投票

这是由建议接口本身支持的,并且它们各自提供了两种实现此目的的方法。

您可以通过它们所在的包或控制器注释来区分两者。

因此,如果您的类由

com.myapp.controller.web
com.myapp.controller.rest
等包分隔,您可以分别使用
@ControllerAdvice("com.myapp.controller.web")
@RestControllerAdvice("com.myapp.controller.rest")

如果您想让注释来驱动它,您只需使用

@ControllerAdvice(annotations = Controller.class)
@RestControllerAdvice(annotations = RestController.class)

© www.soinside.com 2019 - 2024. All rights reserved.