
理解HTTP 406 Not Acceptable 错误
在spring restful api开发中,http 406 not acceptable 错误是一个常见的挑战,尤其是在处理客户端与服务器之间的内容协商时。当客户端发送一个请求,并在其accept请求头中声明了它能够处理的媒体类型(例如application/json或application/xml),而服务器端却无法生成任何符合这些媒体类型的响应时,就会返回406状态码。
错误堆栈信息通常会显示org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation,这明确指出Spring MVC的消息转换器无法找到一个可以将控制器方法返回值转换为客户端可接受格式的处理器。即使项目中引入了Jackson等JSON处理库的依赖,如果Spring无法正确识别或应用这些转换器,问题依然会存在。
错误原因分析
Spring MVC通过HttpMessageConverter接口处理HTTP请求和响应体的序列化与反序列化。当一个控制器方法返回一个Java对象时,Spring会尝试使用注册的HttpMessageConverter将其转换为HTTP响应体。这个过程需要考虑以下几个关键因素:
- 客户端的Accept头: 客户端通过此头告诉服务器它期望接收的媒体类型。
- 服务器端支持的媒体类型: 服务器通过其配置(例如@RequestMapping注解的produces属性)声明它能够生成的媒体类型。
- 可用的HttpMessageConverter: Spring容器中注册的转换器,例如MappingJackson2HttpMessageConverter用于JSON,Jaxb2RootElementHttpMessageConverter用于XML等。
当上述任一环节不匹配时,例如客户端期望JSON,但服务器只声明能生产XML,或者虽然有JSON转换器但控制器方法没有明确声明produces JSON,Spring就可能抛出406错误。在提供的案例中,虽然添加了Jackson相关的依赖,但如果客户端期望JSON而Spring默认或尝试生成XML(可能是因为某种配置或依赖冲突导致XML转换器优先级更高),或者控制器方法没有明确指定produces = MediaType.APPLICATION_JSON_VALUE,那么问题依然会存在。
解决方案:明确指定媒体类型
解决406错误的核心在于明确告诉Spring控制器方法能够produces(生产)哪种媒体类型的响应,以及能够consumes(消费)哪种媒体类型的请求。这可以通过在@RequestMapping及其派生注解(如@GetMapping, @PostMapping)上使用produces和consumes属性来实现。
1. 解决GET请求的406错误(produces)
对于返回响应体的GET请求,我们需要使用produces属性来指定控制器方法能够生成的媒体类型。例如,如果期望返回JSON格式的响应,应将produces设置为MediaType.APPLICATION_JSON_VALUE。
示例代码:
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
@RestController
@EnableWebMvc
@RequestMapping("/api")
@Log4j2
public class UsersController {
@Autowired
UsersService service;
@ResponseBody
@GetMapping(value = "/users",
produces = MediaType.APPLICATION_JSON_VALUE) // 明确指定生成JSON
public ResponseEntity findUById(@RequestParam Long userID) {
// ... 业务逻辑 ...
return service.getUsersInfoById(userID);
}
// ... 其他方法 ...
} 通过添加produces = MediaType.APPLICATION_JSON_VALUE,我们明确告诉Spring,findUById方法将生成JSON格式的响应。当客户端发送带有Accept: application/json头的请求时,Spring就能够找到MappingJackson2HttpMessageConverter来处理响应。
2. 解决POST请求的406/415错误(consumes & produces)
对于POST请求,通常涉及到接收请求体并返回响应体。因此,除了produces,我们还需要考虑consumes属性,它指定了控制器方法能够接受的请求体媒体类型。如果客户端发送的Content-Type头与consumes不匹配,可能会导致415 Unsupported Media Type 错误。
示例代码:
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
@RestController
@EnableWebMvc
@RequestMapping("/api")
@Log4j2
public class UsersController {
@Autowired
UsersService service;
@PostMapping(value = "/users",
consumes = MediaType.APPLICATION_JSON_VALUE, // 明确指定消费JSON请求体
produces = MediaType.APPLICATION_JSON_VALUE) // 明确指定生成JSON响应体
public Users createU(@RequestBody UsersPostRequestBody ubody) {
// ... 业务逻辑 ...
return service.save(ubody);
}
// ... 其他方法 ...
}在这里,consumes = MediaType.APPLICATION_JSON_VALUE确保控制器方法只接受JSON格式的请求体,而produces = MediaType.APPLICATION_JSON_VALUE则确保响应也是JSON格式。
依赖管理与注意事项
虽然明确指定produces和consumes是解决406错误的关键,但正确的依赖管理也是必不可少的。为了支持JSON处理,jackson-databind是核心依赖:
com.fasterxml.jackson.core jackson-databind ${jackson.version} com.fasterxml.jackson.core jackson-core ${jackson.version} com.fasterxml.jackson.annotations jackson-annotations ${jackson.version}
而jackson-dataformat-xml只有在需要处理XML时才应添加。在尝试获取JSON响应时,如果添加了XML相关的Jackson依赖,可能会导致Spring默认尝试使用XML转换器,从而使问题变得复杂。因此,仅在需要时引入相关依赖,并始终通过produces和consumes明确声明意图,是最佳实践。
总结
HTTP 406 Not Acceptable 错误在Spring REST API中通常是内容协商失败的信号。解决此问题的关键在于:
- 理解HTTP内容协商机制,特别是客户端的Accept头与服务器端的Content-Type头。
- 在Spring控制器方法中明确使用produces属性来声明该方法能够生成哪些媒体类型的响应。
- 对于接收请求体的API,使用consumes属性来声明该方法能够接受哪些媒体类型的请求体。
- 确保项目中包含了必要的HttpMessageConverter依赖,例如用于JSON的jackson-databind。
通过遵循这些实践,开发者可以构建更加健壮和可预测的Spring RESTful API,有效避免内容协商相关的错误,确保API能够按照预期提供和消费数据。










