You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何阻止Spring根据Accept Header生成白标错误页面,始终返回JSON响应

问题:Spring Boot REST API强制始终返回JSON错误响应,避免白标页面

我有一个生成JSON响应的Spring Boot REST API应用,控制器通过@ExceptionHandler结合response.sendError来处理自定义异常:

@ExceptionHandler(MyApiException.class)
public void handleControllerException(MyApiException ex, HttpServletResponse response) throws IOException {
    response.sendError(ex.getStatus().value(), ex.getResponseMessage());
}

正常情况下会返回标准的JSON错误响应:

{"timestamp":"2018-01-30T11:22:33.456Z", "status":400, "error":"Bad Request", "message":"No customer with ID 123 found", "path":"/my/api/endpoint"}

但当客户端发送包含text/html的Accept头时(比如浏览器手动调试时),Spring会自动回退到HTML白标错误页面(纯文本展示如下):

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Tue Jan 30 11:22:33 CET 2018
There was an unexpected error (type=Bad Request, status=400). No customer with ID 123 found

我希望禁用这个行为,让Spring始终返回JSON格式的错误响应。虽然找到了一些覆盖或禁用Spring错误页面的通用方法,但这些方法大多需要实现自定义错误页面,改动较大。这只是浏览器手动调试时的小问题(实际客户端不会发送这类Accept头),所以不想对应用做大改动,有没有简单的实现方式?


解决方案

最简洁的方案:提前设置响应Content-Type

直接在调用sendError之前,强制把响应的Content-Type设为application/json,这样Spring的错误处理机制就会优先返回JSON格式,不会切换到HTML白标页面。只需要加一行代码,完全不需要额外配置:

@ExceptionHandler(MyApiException.class)
public void handleControllerException(MyApiException ex, HttpServletResponse response) throws IOException {
    // 强制设置响应类型为JSON
    response.setContentType(MediaType.APPLICATION_JSON_VALUE);
    response.sendError(ex.getStatus().value(), ex.getResponseMessage());
}

这个方法亲测有效,完美匹配你的需求——改动极小,只针对异常处理逻辑做微调,不会影响应用其他部分。


备选方案:手动构建JSON响应返回

如果你想完全绕过Spring的默认错误处理逻辑,可以自己构建符合格式的JSON响应体,直接写入输出流:

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.time.Instant;

@ExceptionHandler(MyApiException.class)
public void handleControllerException(MyApiException ex, HttpServletResponse response) throws IOException {
    response.setContentType(MediaType.APPLICATION_JSON_VALUE);
    response.setStatus(ex.getStatus().value());

    // 获取当前请求路径
    String requestPath = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
            .getRequest().getRequestURI();

    // 构建和Spring默认格式一致的JSON响应
    String jsonResponse = String.format(
        "{\"timestamp\":\"%s\", \"status\":%d, \"error\":\"%s\", \"message\":\"%s\", \"path\":\"%s\"}",
        Instant.now().toString(),
        ex.getStatus().value(),
        ex.getStatus().getReasonPhrase(),
        ex.getResponseMessage(),
        requestPath
    );

    response.getWriter().write(jsonResponse);
}

这种方式完全自主控制响应内容,不用担心Spring的视图解析逻辑干扰,但需要自己拼接JSON(或者用Jackson序列化一个错误DTO会更优雅)。


配置级方案:强制错误响应优先返回JSON

如果不想修改现有ExceptionHandler代码,可以通过配置Spring的错误视图解析器,让它始终返回JSON视图:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.accept.ContentNegotiationManagerFactoryBean;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import org.springframework.boot.autoconfigure.web.servlet.error.DefaultErrorViewResolver;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import java.util.Map;

@Configuration
public class ErrorResponseConfig {

    @Bean
    public ContentNegotiationManager contentNegotiationManager() {
        ContentNegotiationManagerFactoryBean factory = new ContentNegotiationManagerFactoryBean();
        // 设置默认响应类型为JSON
        factory.setDefaultContentType(MediaType.APPLICATION_JSON);
        factory.setFavorPathExtension(false);
        factory.addMediaType("json", MediaType.APPLICATION_JSON);
        return factory.getObject();
    }

    @Bean
    public DefaultErrorViewResolver errorViewResolver(ContentNegotiationManager contentNegotiationManager) {
        return new DefaultErrorViewResolver() {
            @Override
            public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
                // 直接返回JSON视图,忽略HTML解析
                ModelAndView mv = new ModelAndView(new MappingJackson2JsonView());
                mv.addAllObjects(model);
                mv.setStatus(status);
                return mv;
            }
        };
    }
}

这个方案属于全局配置,一次设置后所有错误响应都会返回JSON,适合需要统一控制错误格式的场景。


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

火山引擎 最新活动