You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何替换Spring Boot中的默认错误控制器?

自定义Spring Boot的/error JSON响应控制器问题

我原本以为可以直接编写自己的/error控制器,结果Spring Boot并不允许。以下是最小可复现示例(MRE):

package com.example.helloworldmre.controller;

import com.example.helloworldmre.data.FailureMessage;
import org.springframework.context.annotation.Primary;
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
@RequestMapping("/error")
@Primary
public class ErrorController {
    @RequestMapping
    public FailureMessage errorResponse(HttpServletRequest request) {
        return new FailureMessage(HttpMethod.valueOf(request.getMethod()), request.getRequestURI()); // 一个简单的包含错误信息的类,为简洁起见不贴代码
    }
}

报错信息如下:

Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'basicErrorController' method 
org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
to { [/error]}: There is already 'errorController' bean method
com.example.helloworldmre.controller.ErrorController#errorResponse(HttpServletRequest) mapped.
    at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.validateMethodMapping(AbstractHandlerMethodMapping.java:669) ~[spring-webmvc-5.3.16.jar:5.3.16]
    ... 50 common frames omitted

我尝试禁用白标错误端点,但没有效果:

server.error.whitelabel.enabled=false

我还尝试继承BasicErrorController:

package com.example.helloworldmre.controller;

import com.example.helloworldmre.data.FailureMessage;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

public class ErrorController extends BasicErrorController {
    private final ObjectMapper objectMapper;

    public ErrorController(ObjectMapper objectMapper, ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
        super(errorAttributes, errorProperties);
        this.objectMapper = objectMapper;
    }

    @Override
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        FailureMessage failureMessage = new FailureMessage(HttpMethod.valueOf(request.getMethod()), request.getRequestURI());
        Map<String, Object> failureMessageAsMap = objectMapper.convertValue(failureMessage, new TypeReference<>() {
        });
        return ResponseEntity.badRequest().body(failureMessageAsMap);
    }
}

结果出现以下错误:

2023-12-07 14:14:27.965 ERROR 6336 --- [nio-8090-exec-2] o.a.c.c.C.[Tomcat].[localhost]           : Exception Processing ErrorPage[errorCode=0, location=/error]

javax.servlet.ServletException: Circular view path [error]: would dispatch back to the current handler URL [/error] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
    ... 50 common frames omitted

需求说明:我希望返回简单的JSON格式响应,而非完整的HTML视图,请问正确的实现方式是什么?


正确实现方式

方法1:实现ErrorController接口并替换默认Bean

Spring Boot允许通过实现ErrorController接口自定义错误处理,同时需将类注册为Bean,通过排除默认BasicErrorController避免冲突:

package com.example.helloworldmre.controller;

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

import javax.servlet.http.HttpServletRequest;

@RestController
@Primary
public class CustomErrorController implements ErrorController {

    private static final String ERROR_PATH = "/error";

    @RequestMapping(value = ERROR_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
    public FailureMessage handleError(HttpServletRequest request) {
        return new FailureMessage(
                HttpMethod.valueOf(request.getMethod()),
                request.getRequestURI()
        );
    }

    // Spring Boot 2.3+ 已弃用 getErrorPath(),无需实现
}

同时在启动类中排除默认自动配置:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;

@SpringBootApplication(exclude = ErrorMvcAutoConfiguration.class)
public class HelloWorldMreApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloWorldMreApplication.class, args);
    }
}

方法2:使用@RestControllerAdvice全局异常处理(推荐)

若仅需统一处理异常并返回JSON,无需接管/error端点,全局异常处理器更灵活:

package com.example.helloworldmre.controller;

import com.example.helloworldmre.data.FailureMessage;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;

@RestControllerAdvice
public class GlobalExceptionHandler {

    // 处理所有未捕获的异常
    @ExceptionHandler(Exception.class)
    public ResponseEntity<FailureMessage> handleAllExceptions(HttpServletRequest request, Exception ex) {
        FailureMessage failureMessage = new FailureMessage(
                HttpMethod.valueOf(request.getMethod()),
                request.getRequestURI()
        );
        return ResponseEntity.status(500).body(failureMessage);
    }

    // 针对特定异常单独处理
    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<FailureMessage> handleIllegalArgument(HttpServletRequest request, IllegalArgumentException ex) {
        FailureMessage failureMessage = new FailureMessage(
                HttpMethod.valueOf(request.getMethod()),
                request.getRequestURI()
        );
        return ResponseEntity.badRequest().body(failureMessage);
    }
}

@RestControllerAdvice默认返回JSON,Spring Boot会自动将异常转发到处理器,无需修改/error端点。

方法3:正确继承BasicErrorController

若必须继承BasicErrorController,需注册为Bean并指定返回JSON格式,避免视图解析冲突:

package com.example.helloworldmre.controller;

import com.example.helloworldmre.data.FailureMessage;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

@RestController
@Primary
public class CustomErrorController extends BasicErrorController {

    private final ObjectMapper objectMapper;

    public CustomErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties, ObjectMapper objectMapper) {
        super(errorAttributes, errorProperties);
        this.objectMapper = objectMapper;
    }

    @Override
    @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        FailureMessage failureMessage = new FailureMessage(
                HttpMethod.valueOf(request.getMethod()),
                request.getRequestURI()
        );
        Map<String, Object> response = objectMapper.convertValue(failureMessage, Map.class);
        int statusCode = getStatus(request).value();
        return ResponseEntity.status(statusCode).body(response);
    }
}

此方式明确指定返回JSON,避免视图解析器寻找"error"视图文件,解决循环路径问题。


内容的提问来源于stack exchange,提问作者Sergey Zolotarev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 15:02:15