Spring boot 404错误自定义错误响应ReST

时间:2015-06-18 14:11:48

标签: java json spring rest spring-boot

我使用Spring启动来托管REST API。我希望始终发送JSON响应,而不是标准的错误响应,即使浏览器正在访问URL以及自定义数据结构。

我可以使用@ControllerAdvice和@ExceptionHandler来执行自定义异常。但我无法找到任何好的方法来处理标准和处理错误,如404和401。

有没有什么好的模式可以做到这一点?

11 个答案:

答案 0 :(得分:4)

404错误由DispatcherServlet处理。有一个属性throwExceptionIfNoHandlerFound,你可以覆盖它。

在Application类中,您可以创建一个新bean:

@Bean
DispatcherServlet dispatcherServlet () {
    DispatcherServlet ds = new DispatcherServlet();
    ds.setThrowExceptionIfNoHandlerFound(true);
    return ds;
}

...然后在

中捕获NoHandlerFoundException异常
@EnableWebMvc
@ControllerAdvice
public class GlobalControllerExceptionHandler {
    @ExceptionHandler
    @ResponseStatus(value=HttpStatus.NOT_FOUND)
    @ResponseBody
    public ErrorMessageResponse requestHandlingNoHandlerFound(final NoHandlerFoundException ex) {
        doSomething(LOG.debug("text to log"));
    }
}

答案 1 :(得分:3)

我提供了有关如何覆盖404案例响应的示例解决方案。解决方案非常简单,我发布了示例代码,但您可以在原始帖子中找到更多详细信息:Spring Boot Rest - How to configure 404 - resource not found

首先:定义将处理错误案例并覆盖响应的控制器:

@ControllerAdvice
public class ExceptionHandlerController {

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(value= HttpStatus.NOT_FOUND)
    @ResponseBody
    public ErrorResponse requestHandlingNoHandlerFound() {
        return new ErrorResponse("custom_404", "message for 404 error code");
    }
}

第二次:你需要告诉Spring在404的情况下抛出异常(无法解析处理程序):

@SpringBootApplication
@EnableWebMvc
public class Application {

    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(Application.class, args);

        DispatcherServlet dispatcherServlet = (DispatcherServlet)ctx.getBean("dispatcherServlet");
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
    }
}

答案 2 :(得分:3)

在@RestControllerAdvice with spring boot 的情况下对我有用

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

@RestControllerAdvice
public class ErrorHandlerController {

@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(value = HttpStatus.NOT_FOUND )
public String handleNotFoundError(NoHandlerFoundException ex) {
    return "path does not exists";
}
}

答案 3 :(得分:2)

总结所有答案和评论,我认为做到这一点的最佳方法是-

首先,告诉Spring Boot在application.properties中找不到处理程序的情况下引发异常。

spring.mvc.throw-exception-if-no-handler-found=true

然后在应用程序中处理NoHandlerFoundException。我按照以下方式处理

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(NoHandlerFoundException.class)
    public void handleNotFoundError(HttpServletResponse response, NoHandlerFoundException ex) {
        ErrorDto errorDto = Errors.URL_NOT_FOUND.getErrorDto();
        logger.error("URL not found exception: " + ex.getRequestURL());
        prepareErrorResponse(response, HttpStatus.NOT_FOUND, errorDto);
    }
}

答案 4 :(得分:1)

您可以添加与web.xml中的错误页面定义相关的自定义 ErrorPage 对象。 Spring Boot provides an example ...

@Bean
public EmbeddedServletContainerCustomizer containerCustomizer(){
    return new MyCustomizer();
}

// ...

private static class MyCustomizer implements EmbeddedServletContainerCustomizer {

    @Override
    public void customize(ConfigurableEmbeddedServletContainer container) {
        container.addErrorPages(new ErrorPage(HttpStatus.UNAUTHORIZED, "/unauthorized.html"));
        container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/not-found.html"));
    }

}

编辑虽然我认为如果您将错误页面设置为控制器,上面的方法将会起作用,但更简单的方法是包含一个自定义的 ErrorController 下面...

@Bean
public ErrorController errorController(ErrorAttributes errorAttributes) {
    return new CustomErrorController(errorAttributes);
}

// ...

public class CustomErrorController extends BasicErrorController {

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

    @Override
    @RequestMapping(value = "${error.path:/error}")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        ResponseEntity<Map<String, Object>> error = super.error(request);
        HttpStatus statusCode = error.getStatusCode();

        switch (statusCode) {
        case NOT_FOUND:
            return getMyCustomNotFoundResponseEntity(request);
        case UNAUTHORIZED:
            return getMyCustomUnauthorizedResponseEntity(request);
        default:
            return error;
        }
    }
}

答案 5 :(得分:1)

对于那些不想使用@EnableWebMvc的Spring Boot 2用户

application.properties

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

ControllerAdvice

@RestControllerAdvice
public class ExceptionResolver {

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public HashMap<String, String> handleNoHandlerFound(NoHandlerFoundException e, WebRequest request) {
        HashMap<String, String> response = new HashMap<>();
        response.put("status", "fail");
        response.put("message", e.getLocalizedMessage());
        return response;
    }
}

Source

答案 6 :(得分:1)

您可以扩展 ResponseEntityExceptionHandler 类,其中包含 Spring Boot 项目中的许多常见异常。例如,如果您希望使用自定义处理程序来绑定异常,您可以使用以下内容,

@ControllerAdvice
public class MyApiExceptionHandler extends ResponseEntityExceptionHandler {

    @Override
    public ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        String responseBody = "{\"key\":\"value\"}";
        headers.add("Content-Type", "application/json;charset=utf-8");
        return handleExceptionInternal(ex, responseBody, headers, HttpStatus.NOT_ACCEPTABLE, request);
    }
}

http 状态的另一个示例 404-Not Found,

@ControllerAdvice
public class MyApiExceptionHandler extends ResponseEntityExceptionHandler {

    @Override
    public ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        String responseBody = "{\"errormessage\":\"WHATEVER YOU LIKE\"}";
        headers.add("Content-Type", "application/json;charset=utf-8");
        return handleExceptionInternal(ex, responseBody, headers, HttpStatus.NOT_FOUND, request);
    }
}

关于 404 not found 异常,您应该将 DispatcherServlet 配置为在未找到任何处理程序时抛出异常,而不是默认行为。对于 404 问题,您还可以阅读this 问题。

答案 7 :(得分:0)

您似乎需要引入适当注释的方法,例如:对于不支持的媒体类型(415),它将是:

  @ExceptionHandler(MethodArgumentNotValidException)
  public ResponseEntity handleMethodArgumentNotValidException(HttpServletRequest req, MethodArgumentNotValidException e) {
    logger.error('Caught exception', e)
    def response = new ExceptionResponse(
            error: 'Validation error',
            exception: e.class.name,
            message: e.bindingResult.fieldErrors.collect { "'$it.field' $it.defaultMessage" }.join(', '),
            path: req.servletPath,
            status: BAD_REQUEST.value(),
            timestamp: currentTimeMillis()
    )
    new ResponseEntity<>(response, BAD_REQUEST)
  }

然而,可能无法实现,因为401和404可能会在到达DispatcherServlet之前被抛出 - 在这种情况下ControllerAdvice将不起作用。

答案 8 :(得分:0)

请参阅Spring Boot REST service exception handling。它显示了如何告诉dispatcherservlet为“找不到路由”发出异常,然后告诉如何捕获这些异常。我们(我工作的地方)现在正在生产我们的REST服务。

答案 9 :(得分:0)

从Spring版本5开始,可以使用类ResponseStatusException:

@GetMapping("example")
public ResponseEntity example() {
    try {
        throw new MyException();
    } catch (MyException e) {
        throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "My Exception", e);
    }
}

答案 10 :(得分:-1)

我希望在所有可能的错误情况下都具有相同的错误格式(json)结构,因此我只是重用了AbstractErrorController的代码,注册了自己的ErrorController:

@Controller
@RequestMapping(path = "/error", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public class ErrorController extends AbstractErrorController {

public ErrorController(ErrorAttributes errorAttributes,
                       ObjectProvider<ErrorViewResolver> errorViewResolvers) {
    super(errorAttributes, errorViewResolvers.orderedStream().collect(Collectors.toUnmodifiableList()));
}

@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
    final var status = getStatus(request);
    if (status == HttpStatus.NO_CONTENT) {
        return new ResponseEntity<>(status);
    }
    return new ResponseEntity<>(getErrorAttributes(request, ErrorAttributeOptions.defaults()), status);
}

@Override
public String getErrorPath() {
    return null;
}

}

与此相关,您不需要任何控制器建议,默认情况下所有错误都将转到错误方法