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

Spring RestController返回HTML错误而非JSON的问题求助

恢复Spring Boot升级后REST API的JSON错误响应

我之前升级Spring Boot、Spring MVC和Spring Security到最新版本时,也踩过一模一样的坑——原本默认返回的JSON格式错误响应突然变成了Tomcat的HTML错误页面。折腾了一番后,找到了几个靠谱的解决思路,分享给你:

1. 先检查请求的Accept头

新版本的Spring Boot错误处理会根据请求的Accept头自动协商响应格式。如果你的API客户端没有明确设置Accept: application/json,服务器可能会默认返回HTML(比如浏览器请求时Accept包含text/html)。

  • 解决方法:确保你的API请求头里带上Accept: application/json;如果没法修改客户端请求,可以通过配置强制服务器返回JSON。

2. 自定义错误处理控制器(最直接的方案)

自定义一个ErrorController接管所有错误请求,强制返回JSON格式的响应,和你升级前的格式完全一致:

import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;

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

@RestController
@RequestMapping("/error")
public class CustomJsonErrorController {

    private final ErrorAttributes errorAttributes;

    public CustomJsonErrorController(ErrorAttributes errorAttributes) {
        this.errorAttributes = errorAttributes;
    }

    @RequestMapping
    public ResponseEntity<Map<String, Object>> handleError(HttpServletRequest request) {
        WebRequest webRequest = new ServletWebRequest(request);
        // 获取默认错误属性:timestamp、status、error、message、path
        Map<String, Object> errorDetails = errorAttributes.getErrorAttributes(
                webRequest,
                ErrorAttributeOptions.of(ErrorAttributeOptions.Include.MESSAGE)
        );
        HttpStatus status = getHttpStatus(request);
        return new ResponseEntity<>(errorDetails, status);
    }

    private HttpStatus getHttpStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode != null) {
            try {
                return HttpStatus.valueOf(statusCode);
            } catch (IllegalArgumentException e) {
                return HttpStatus.INTERNAL_SERVER_ERROR;
            }
        }
        return HttpStatus.INTERNAL_SERVER_ERROR;
    }
}

这个控制器会接管Spring Boot默认的/error错误跳转路径,把错误信息统一包装成JSON返回。

3. 配置内容协商,优先返回JSON

通过Spring Boot配置修改内容协商策略,让服务器在请求未指定Accept头时,默认返回JSON:

application.yml 配置

spring:
  mvc:
    contentnegotiation:
      favor-parameter: false
      favor-path-extension: false
      default-content-type: application/json
      media-types:
        json: application/json

application.properties 配置

spring.mvc.contentnegotiation.favor-parameter=false
spring.mvc.contentnegotiation.favor-path-extension=false
spring.mvc.contentnegotiation.default-content-type=application/json
spring.mvc.contentnegotiation.media-types.json=application/json

4. 处理Spring Security的异常拦截

如果项目用了Spring Security,升级后可能是Security的异常处理覆盖了默认错误响应。可以在Security配置中自定义认证/授权失败的JSON响应:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final ObjectMapper objectMapper;

    public SecurityConfig(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            // 其他Security配置...
            .exceptionHandling(exceptionHandling ->
                exceptionHandling
                    .authenticationEntryPoint((request, response, authException) -> {
                        // 认证失败返回JSON
                        sendJsonErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized", authException.getMessage());
                    })
                    .accessDeniedHandler(accessDeniedHandler())
            );
        return http.build();
    }

    private AccessDeniedHandler accessDeniedHandler() {
        return (request, response, accessDeniedException) -> {
            // 授权失败返回JSON
            sendJsonErrorResponse(response, HttpServletResponse.SC_FORBIDDEN, "Forbidden", accessDeniedException.getMessage());
        };
    }

    private void sendJsonErrorResponse(HttpServletResponse response, int statusCode, String error, String message) throws IOException {
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setStatus(statusCode);
        Map<String, Object> errorResponse = new HashMap<>();
        errorResponse.put("timestamp", System.currentTimeMillis());
        errorResponse.put("status", statusCode);
        errorResponse.put("error", error);
        errorResponse.put("message", message);
        errorResponse.put("path", ((HttpServletRequest) response.getRequest()).getRequestURI());
        objectMapper.writeValue(response.getOutputStream(), errorResponse);
    }
}

5. 禁用Whitelabel错误页面(可选)

如果以上方案都没生效,可以尝试禁用Spring Boot默认的Whitelabel错误页面:

server.error.whitelabel.enabled=false

注意:这个配置需要配合自定义错误控制器一起使用,否则可能会返回空白页面或Tomcat默认页面。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:42:02