如何返回自定义错误 JSON 来代替 Spring Boot 提供的默认错误 JSON?

问题描述 投票:0回答:1

要使其成为完整的 MRE,只需使用 Web 启动器 Lombok 创建一个基本的 Boot 项目并包含以下类。在我的项目中,我使用 Spring Boot 2.6.4,但我相信这对于这个问题的目的来说并不重要

如果您的 Web 应用程序中未包含任何自定义异常逻辑并向不存在的端点发出请求,您将获得与此类似的 JSON(假设请求的标头不包含

Accept: text/html
,在在这种情况下,您将获得一个白标签 html 页面):

{
  "timestamp": "2024-02-09T05:02:23.763+00:00",
  "status": 404,
  "error": "Not Found",
  "path": "/bogus-path"
}

这还不错,但是如果我想要更多的控制权怎么办(我并不是说用像

server.error.include-stacktrace
这样的属性稍微调整 JSON)?

为了拥有自己的异常处理逻辑,我编写了自己的简单

ErrorController

import com.example.helloworldmre.data.FailureMessage;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

@RestController
public class MyErrorController implements ErrorController {
    @RequestMapping("/error")
    public FailureMessage error(HttpServletRequest request) {
        return new FailureMessage(HttpMethod.valueOf(request.getMethod()), request.getRequestURI());
    }
}
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import org.springframework.http.HttpMethod;

@Getter
public class FailureMessage extends Message {
    @JsonProperty("request_path")
    private final String requestPath;
    private final HttpMethod method;
    public FailureMessage(HttpMethod method, String requestPath) {
        super("Could not perform the request");
        this.method = method;
        this.requestPath = requestPath;
    }
}
import lombok.Getter;

@Getter
public abstract class Message {
    private final String message;

    public Message(String message) {
        this.message = message;
    }
}

我的目的是让它在每次发生不好的事情时返回这样的响应(例如对不存在的端点的请求):

{
  "message": "Could not perform the request",
  "method": "POST",
  "request_path": "/some-bogus-path"
}

相反,它总是返回以下响应:

{
  "message": "Could not perform the request",
  "method": "GET",
  "request_path": "/error"
}

换句话说,我捕获的是重定向的路径和方法,而不是原始请求

这篇Baeldung文章建议你自己写

HandlerExceptionResolver

@Component
public class RestResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver {
    @Override
    protected ModelAndView doResolveException(
      HttpServletRequest request,
      HttpServletResponse response,
      Object handler, 
      Exception ex) {
        return null; // breakpoint here
    }
}

但是断点从未被命中,并且响应与 Boot 中的默认 JSON 相同

我也尝试过这个:

@RestControllerAdvice
public class MyErrorController {
    @ExceptionHandler(Exception.class)
    public FailureMessage error(HttpServletRequest request) {
        return new FailureMessage(HttpMethod.valueOf(request.getMethod()), request.getRequestURI());
    }
}

同样是默认的 JSON。我想,当接收到不存在的端点的请求时,Boot 不会抛出任何异常,因此处理程序不会启动

总而言之,我如何自己处理异常,以涉及访问的方式返回自定义 JSON:

  1. 原始请求路径
  2. 前往请求方法
  3. 了解到底出了什么问题(抛出了什么异常或者引导将异常映射到什么 HTTP 状态)
java spring-boot spring-mvc
1个回答
0
投票

在 Boot 中,正确的方法是扩展

DefaultErrorAttributes
并编译您自己的属性映射,如下所示:

package com.example.helloworldmre.config;

import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;

import javax.servlet.RequestDispatcher;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;

@Component
public class CustomErrorAttributes extends DefaultErrorAttributes {
    @Override
    public Map<String, Object> getErrorAttributes(
            WebRequest webRequest, ErrorAttributeOptions options) {
        Map<String, Object> errorAttributes =
          new LinkedHashMap<>();

        addError(errorAttributes, webRequest);
        addMethod(errorAttributes, webRequest);
        addPath(errorAttributes, webRequest);
        addTimestamp(errorAttributes, webRequest);

        return errorAttributes;
    }

    private void addError(Map<String, Object> errorAttributes, WebRequest webRequest) {
        Optional<Integer> optionalStatus = Optional.ofNullable(
                (Integer) webRequest.getAttribute(RequestDispatcher.ERROR_STATUS_CODE, RequestAttributes.SCOPE_REQUEST)
        );
        String errorDescription;

        if (optionalStatus.isPresent()) {
            Integer statusCode = optionalStatus.get();
            String reasonPhrase = HttpStatus.valueOf(statusCode).getReasonPhrase();
            errorDescription = MessageFormat.format("{0} {1}", statusCode, reasonPhrase);
        } else errorDescription = "Unknown";

        errorAttributes.put("error", errorDescription);
    }

    private void addMethod(Map<String, Object> errorAttributes, WebRequest webRequest) {
        errorAttributes.put("method", ((ServletWebRequest) webRequest).getHttpMethod());
    }

    private void addPath(Map<String, Object> errorAttributes, WebRequest webRequest) {
        Object path = webRequest.getAttribute(RequestDispatcher.ERROR_REQUEST_URI, RequestAttributes.SCOPE_REQUEST);
        errorAttributes.put("path", path);
    }

    private void addTimestamp(Map<String, Object> errorAttributes, WebRequest webRequest) {
        errorAttributes.put("timestamp", LocalDateTime.now());
    }
}

现在,我的错误 JSON 看起来像这样

{
  "error": "404 Not Found",
  "method": "GET",
  "path": "/bogus-path",
  "timestamp": "2024-02-09T10:09:31.1491971"
}

尽管我仍然感觉自己是在遵守 Spring Boot 的规则

© www.soinside.com 2019 - 2024. All rights reserved.