[英]Spring boot 404 error custom error response ReST
我正在使用 Spring Boot 来托管 REST API。 即使浏览器正在访问 URL 以及自定义数据结构,我也希望始终发送 JSON 响应,而不是标准错误响应。
对于自定义异常,我可以使用 @ControllerAdvice 和 @ExceptionHandler 来做到这一点。 但是对于像 404 和 401 这样的标准和已处理错误,我找不到任何好的方法来执行此操作。
有没有什么好的模式可以做到这一点?
对于那些不想使用@EnableWebMvc
Spring Boot 2 用户
应用程序属性
server.error.whitelabel.enabled=false
spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
控制器建议
@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;
}
}
我已经提供了有关如何覆盖 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);
}
}
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"));
}
}
总结所有答案和评论,我认为最好的方法是-
首先,告诉 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);
}
}
在@RestControllerAdvice 和 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";
}
}
我遇到了同样的问题,但使用不同的方法修复了它。 要在自定义响应中返回404 、 401和其他状态,您现在可以将响应状态添加到自定义异常类并从您的异常处理程序中调用它。
使用 spring 实用程序类AnnotationUtils ,您可以使用 findAnnotation 方法获取任何定义的自定义异常的状态,并且它将使用您为异常定义的任何注释(包括未找到)返回适当的状态。
这是我的@RestControllerAdvice
@RestControllerAdvice public class MainExceptionHandler extends Throwable{ @ExceptionHandler(BaseException.class) ResponseEntity<ExceptionErrorResponse> exceptionHandler(GeneralMainException e) { ResponseStatus status = AnnotationUtils.findAnnotation(e.getClass(),ResponseStatus.class); if(status != null) { return new ResponseEntity<>(new ExceptionErrorResponse(e.getCode(),e.getMessage()),status.code()); } }
CustomParamsException 返回错误的请求状态
@ResponseStatus(value= HttpStatus.BAD_REQUEST) public class CustomParamsException extends BaseException { private static final String CODE = "400"; public CustomParamsException(String message) { super(CODE, message); } }
未找到详细信息以返回未找到状态
@ResponseStatus(value= HttpStatus.NOT_FOUND) public class DetailsNotException extends BaseException { private static final String CODE = "400"; public DetailsNotException(String message) { super(CODE, message); } }
用于扩展 Excetion 的 GeneralMainException
public class GeneralMainException extends Exception { private String code; private String message; public GeneralMainException (String message) { super(message); } public GeneralMainException (String code, String message) { this.code = code; this.message = message; } public String getCode() { return code; } @Override public String getMessage() { return message; } }
您可以通过将其包含在控制器建议中来决定处理其他系统异常。
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
ExceptionErrorResponse sysError(Exception e)
{
return new ExceptionErrorResponse(""1002", e.getMessage());
}
您可以扩展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 的问题,您还可以阅读此问题。
似乎您需要引入一个适当注释的方法,例如对于不受支持的媒体类型(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
将不起作用。
您可以添加与 web.xml 中的错误页面定义相关的自定义ErrorPage对象。 Spring Boot 提供了一个示例...
@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;
}
}
}
请参阅Spring Boot REST 服务异常处理。 它展示了如何告诉 dispatcherservlet 为“未找到路由”发出异常,然后如何捕获这些异常。 我们(我工作的地方)现在正在为我们的 REST 服务在生产中使用它。
从 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);
}
}
我想在所有可能的错误场景中使用相同的错误格式 (json) 结构,所以我只是注册了我自己的 ErrorController,重用了 AbstractErrorController 中的代码:
@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;
}
}
有了这个你不需要任何控制器建议,默认情况下所有错误都转到错误方法
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.