如何在 Spring Boot 中发送 400、401、405、403 和 500 错误的自定义响应?

Posted

技术标签:

【中文标题】如何在 Spring Boot 中发送 400、401、405、403 和 500 错误的自定义响应?【英文标题】:How to send custom response for 400, 401, 405, 403 and 500 errors in spring boot? 【发布时间】: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 或任何自定义内容。 【参考方案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

【讨论】:

以上是关于如何在 Spring Boot 中发送 400、401、405、403 和 500 错误的自定义响应?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Spring Boot 中覆盖默认 JSON 响应

甚至在请求到达过滤器之前,在 Spring Boot 中返回 400 错误请求

Spring Boot 和 Thymeleaf 在字段错误时返回 400 错误

如何在 spring-boot Web 客户端中发送请求正文?

在 Spring Boot 中处理嵌入式 Tomcat 异常

WebSocket握手期间出错:意外的响应代码:400 Spring boot websockets