【问题标题】:Spring Boot Rest Controller: Return default Error JSONSpring Boot Rest Controller:返回默认错误 JSON
【发布时间】:2019-09-26 03:36:34
【问题描述】:

在我的 API 中,我喜欢保护用户详细信息端点,以便正常登录的用户只能访问他们的用户资料。因此我正在编写控制器:

@RequestMapping(value = URL_USER + "/{id}", method = RequestMethod.GET)
@ResponseBody
public PersistentEntityResource get(PersistentEntityResourceAssembler persistentEntityResourceAssembler, @PathVariable Long id) {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

    ApplicationUser loggedInUser = applicationUserService.findByUsername(authentication.getName());
    ApplicationUser applicationUser = applicationUserService.findById(id);

    if (applicationUser.getId().equals(loggedInUser.getId())) {
        return persistentEntityResourceAssembler.toFullResource(applicationUser);
    }

    throw new IllegalAccessException();
} 

与其引发导致InternalServerExcetption 的异常,我更喜欢返回默认的spring boot 错误json,如下所示:

{
    "timestamp": "2019-05-08T11:42:23.064+0000",
    "status": 403,
    "error": "Forbidden",
    "message": "Access Denied",
    "path": "/user/2"
}

我希望有一个解决方案,它也适用于 404 等其他错误。实现该目标的最简单方法是什么?

【问题讨论】:

  • 您可以使用@ControllerAdvice@ExceptionHandler 来实现这一点,就像我在this question 中提到的那样
  • 这行不通,因为我喜欢继续返回 PersistentEntityResource 而不是 ResponseEntity
  • 这样的换行有什么问题吗? ResponseEntity<PersistentEntityResource>
  • 还没试过,不过找到了一个解决方案,貌似最优雅

标签: java rest spring-boot


【解决方案1】:

你也可以使用下面的方法

public class FooController{

    //...
    @ExceptionHandler({ CustomException1.class, CustomException2.class })
    public String handleException() {
        return "the intended body";
    }
}

或者,您可以使用@ControllerAdvice将此逻辑作为全局异常处理程序放入

@ControllerAdvice
public class RestResponseEntityExceptionHandler
        extends ResponseEntityExceptionHandler {

    @ExceptionHandler(value
            = { IllegalArgumentException.class, IllegalStateException.class })
    protected ResponseEntity<Object> handleConflict(
            RuntimeException ex, WebRequest request) {
        String bodyOfResponse = "This should be application specific";
        return handleExceptionInternal(ex, bodyOfResponse,
                new HttpHeaders(), HttpStatus.CONFLICT, request);
    }
}

【讨论】:

    【解决方案2】:

    经过一番研究,我找到了一个看起来相当优雅的解决方案:

    RestController 方法长这样:

    @RequestMapping(value = URL_USER + "/{id}", method = RequestMethod.GET)
    @ResponseBody
    public PersistentEntityResource get(PersistentEntityResourceAssembler persistentEntityResourceAssembler, @PathVariable Long id) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    
        ApplicationUser loggedInUser = applicationUserService.findByUsername(authentication.getName());
        ApplicationUser applicationUser = applicationUserService.findById(id);
    
        if (applicationUser.getId().equals(loggedInUser.getId())) {
            return persistentEntityResourceAssembler.toFullResource(applicationUser);
        }
    
        throw new ForbiddenRequestException("Access not allowed");
    }
    

    另外我已经实现了ForbiddenRequestException类:

    @ResponseStatus(value = HttpStatus.FORBIDDEN)
    public class ForbiddenRequestException extends RuntimeException {
        public ForbiddenRequestException(String message) {
            super(message);
        }
    }
    

    至少要通过在属性中设置 server.error.include-stacktrace=never 从 JSON 中删除 trace,这可能并不理想,但我认为在生产中你应该这样做。

    【讨论】:

      猜你喜欢
      • 2015-05-20
      • 1970-01-01
      • 2017-06-27
      • 1970-01-01
      • 2021-08-20
      • 2020-07-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多