【问题标题】:How to send custom response for 400, 401, 405, 403 and 500 errors in spring boot?如何在 Spring Boot 中发送 400、401、405、403 和 500 错误的自定义响应?
【发布时间】:2020-10-20 09:25:21
【问题描述】:

我想在 Spring Boot 中针对 400、401、405、403 和 500 错误发送自定义 JSON 响应。对于这些错误,spring boot 有时会发送一个 HTML 响应,有时会发送一个空的响应体响应,有时会发送一个完整的响应,其中包含时间戳、异常、消息等。我想要所有这些错误的单一且一致的 JSON 响应,就像这样

{
statusCode : 405,
message: "HTTP Method not allowed"
}

我使用@ControllerAdvice 为很多异常发送自定义响应。这些 400、401、405、403 和 500 错误我面临的问题是我不知道当这些错误发生时会引发哪些异常,以便我可以为它们编写 @ExceptionHandlers。即使我不知道这是否可能,或者即使可能,但它是实现这一点的正确方法。

有没有办法在 Spring boot 中实现这个?

【问题讨论】:

  • 查看这里,如何修改默认spring错误信息,dev.to/s2agrahari/…
  • @Suraj 为什么不添加这个作为答案?
  • @SimonMartinelli 好的。我会添加
  • 500 是专门提供的,以便对可能的攻击者隐藏信息。您不应该使用此状态代码提供任何 JSON 或任何自定义内容。

标签: java spring spring-boot


【解决方案1】:

所有传出的异常都由 Spring Boot 处理,BasicErrorController

我们可以使用控制器通知来处理/更改大多数异常消息。但是,在到达控制器之前抛出的异常,如NotFoundBadRequest 等,再次由BasicErrorController 处理

我们也可以通过覆盖 BasicErrorController 来改变它。

@RestController
@RequestMapping({"${server.error.path:${error.path:/error}}"})
@Slf4j
public class BasicErrorControllerOverride extends AbstractErrorController {

  public BasicErrorControllerOverride(ErrorAttributes errorAttributes) {
    super(errorAttributes);
  }

  @RequestMapping
  public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
    HttpStatus status = this.getStatus(request);
    Map<String, Object> errorCustomAttribute = new HashMap<>();
    errorCustomAttribute.put("custome_message", "error");
    errorCustomAttribute.put("custom_status_field", status.name());
    return new ResponseEntity(errorCustomAttribute, status);
  }

  @Override
  public String getErrorPath() {
    return "/error";
  }
}

错误消息现在将更改为如下所示

{
    "custome_message": "UNAUTHORIZED",
    "custom_status_field": "error"
}

【讨论】:

  • 此解决方案不起作用。它会为每个错误抛出 404,因为 @RequestMapping 没有价值。即使我添加了值作为错误路径,它仍然无法满足 400 Bad request 和其他错误。
  • 您能分享您测试的更改吗?该解决方案是在我的本地使用 spring-boot 2.1.8.RELEASE 测试的解决方案
  • 只做了一项更改,即@RequestMapping("/error")
【解决方案2】:

Spring ResponseEntity 可用于此目的 -

return new ResponseEntity<>("Hello World!", HttpStatus.OK);

参考这个链接; https://www.baeldung.com/spring-response-entity

好吧,即使你没有使用 spring,也有一些简单的做法,比如让你的所有响应类都从这样的基类扩展,只是一个例子 -

public class StandardResponse{
    private int httpStatus;
  
    //getters and setters
}

public class ProductResponse extends StandardResponse{
    // product specific data
}

【讨论】:

    【解决方案3】:

    你可以使用 @ControllerAdvice 和 application/problem+json (zalando/problem 是一个很好的 java 实现)

    这是一个例子: https://www.baeldung.com/problem-spring-web

    【讨论】:

    • 我对使用任何第三方库都有限制。
    【解决方案4】:

    与 gaetan224 建议的方式类似,您可以使用 @ControllerAdvice 添加控制器以自定义方式处理异常:

    @ControllerAdvice
    @RequestMapping(produces = "application/json")
    @ResponseBody
    public class RestControllerAdvice {
        
        @Autowired
        Environment env;
    
        @ExceptionHandler(NoHandlerFoundException.class)
        public ResponseEntity<?> unhandledPath(final NoHandlerFoundException e) {
             
            //This is for 405 METHOD NOT ALLOWED
            //Here you may want to return a ResponseEntity with a custom POJO as body.
    
        }
        
        @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
        public ResponseEntity<?> methodNotAllowed(final HttpRequestMethodNotSupportedException e) {
            
            //This is for 404 NOT FOUND
            //Here you may want to return a ResponseEntity with a custom POJO as body.
    
        }
    
    }
    

    在 Spring Security 中处理 403 UNAUTHORIZED 时情况会有所不同。您必须编写一个实现 AccessDeniedHandler 接口的@Component。像这样的:

    @Component
    public class AccessEntryPoint implements AccessDeniedHandler {
    
        @Override
        public void handle(HttpServletRequest req,
                           HttpServletResponse res,
                           AccessDeniedException accessDeniedException) throws IOException, ServletException {
            
            ObjectMapper mapper = new ObjectMapper();
            res.setContentType("application/json;charset=UTF-8");
            res.setStatus(403);
            res.getWriter().write( /*Your custom object may go here*/ );
        }
    }
    

    但这还不够,您还必须使用以下方法在 WebSecurityConfigureAdapter 实现中设置自定义 AccessDeniedHandler 实现:

    @Autowired
    AccessEntryPoint accessDeniedHandler;
    

    并将以下内容附加到您的配置方法调用链中:

    .exceptionHandling().accessDeniedHandler(accessDeniedHandler).and()
    

    编辑:我忘了添加,为了让 404 自定义处理程序工作,您必须在 application.properties 文件中添加这两行:

    spring.resources.add-mappings=false
    spring.mvc.throw-exception-if-no-handler-found=true
    

    【讨论】:

      猜你喜欢
      • 2023-01-25
      • 2012-02-21
      • 1970-01-01
      • 2014-12-01
      • 2021-09-25
      • 2015-09-04
      • 1970-01-01
      • 2021-02-24
      • 2020-03-17
      相关资源
      最近更新 更多