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
相关产品推荐
相关产品推荐

