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

Spring @ExceptionHandler媒体类型不兼容致500错误的解决问询

问题原因

当客户端请求的Accept头为application/octet-stream且实体不存在时,你的全局异常处理器返回的是JSON格式的ErrorResponse,但Spring MVC会校验响应媒体类型是否匹配客户端的Accept要求。由于客户端明确只接受二进制流,Spring找不到兼容的响应转换器,会抛出HttpMediaTypeNotAcceptableException,最终导致服务返回500错误而非预期的404。

解决方案

方案一:全局异常处理器支持多媒体类型(推荐)

修改异常处理器方法,添加produces属性声明支持的媒体类型,并根据请求的Accept头返回对应格式的响应:

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;

import java.nio.charset.StandardCharsets;

@ControllerAdvice
public class ResponseExceptionHandler {

    @ExceptionHandler(EntityNotFoundException.class)
    @GetMapping(produces = {"application/json", "application/octet-stream"})
    public ResponseEntity<?> handleEntityNotFoundException(HttpServletRequest request, EntityNotFoundException e) {
        String acceptHeader = request.getHeader(HttpHeaders.ACCEPT);
        if (MediaType.APPLICATION_OCTET_STREAM_VALUE.equals(acceptHeader)) {
            // 返回二进制格式错误信息
            byte[] errorBytes = e.getMessage().getBytes(StandardCharsets.UTF_8);
            return ResponseEntity.status(HttpStatus.NOT_FOUND)
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(errorBytes);
        } else {
            // 返回JSON格式错误信息
            return ResponseEntity.status(HttpStatus.NOT_FOUND)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(new ErrorResponse().error(e.getMessage()));
        }
    }

    // 兜底处理媒体类型不匹配异常
    @ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
    public ResponseEntity<byte[]> handleMediaTypeNotAcceptable() {
        byte[] errorBytes = "Requested media type not supported".getBytes(StandardCharsets.UTF_8);
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(errorBytes);
    }
}

方案二:强制返回JSON格式错误

如果业务允许忽略客户端的Accept要求,可在异常处理器中强制指定JSON媒体类型,同时处理媒体类型不匹配的异常:

@ControllerAdvice
public class ResponseExceptionHandler {

    @ExceptionHandler(EntityNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleEntityNotFoundException(EntityNotFoundException e) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
                .contentType(MediaType.APPLICATION_JSON)
                .body(new ErrorResponse().error(e.getMessage()));
    }

    @ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
    public ResponseEntity<ErrorResponse> handleMediaTypeNotAcceptable() {
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
                .contentType(MediaType.APPLICATION_JSON)
                .body(new ErrorResponse().error("Entity not found"));
    }
}

方案三:端点内提前处理媒体类型匹配

在下载端点方法中,提前判断实体是否存在,并根据Accept头直接返回对应格式的响应,避免抛出异常后出现媒体类型冲突:

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.core.io.ClassPathResource;

import java.nio.charset.StandardCharsets;

@RestController
public class ResourceController {

    @GetMapping(value = "/{id}", produces = {"application/json", "application/octet-stream"})
    public ResponseEntity<?> download(@PathVariable final int id, @RequestHeader(HttpHeaders.ACCEPT) String accept) {
        if (id % 2 == 0) {
            if (MediaType.APPLICATION_OCTET_STREAM_VALUE.equals(accept)) {
                byte[] errorBytes = "Entity not found".getBytes(StandardCharsets.UTF_8);
                return ResponseEntity.status(HttpStatus.NOT_FOUND)
                        .contentType(MediaType.APPLICATION_OCTET_STREAM)
                        .body(errorBytes);
            } else {
                return ResponseEntity.status(HttpStatus.NOT_FOUND)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(new ErrorResponse().error("Entity not found"));
            }
        }
        return ResponseEntity.ok()
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(new ClassPathResource("application.properties"));
    }
}

方案选择

方案一最符合REST设计规范,既能根据客户端需求返回对应格式的错误信息,又通过全局异常处理器实现了代码复用,是最优解。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 17:40:46