问题背景

在本地环境下,我们以POST方式向Spring Boot应用发起请求,这是一个查询部门信息的地址。

请求信息:http://localhost:8080/depts

当使用接口测试工具进行接口测试后,报错如下:

{
    "timestamp": "2025-04-13T10:46:20.933+00:00",
    "status": 406,
    "error": "Not Acceptable",
    "path": "/depts"
}

从错误信息中可以看到返回状态码为406 Not Acceptable,这表明服务器无法根据请求的内容协商出合适的响应格式,因而返回了错误信息。这一问题通常是由于请求与响应的数据格式或返回对象的序列化问题导致的。接下来我们详细分析该问题的具体原因。

问题分析

在Spring Boot中,406 Not Acceptable错误通常表示服务器找不到与请求Accept头匹配的数据格式,而Accept头指明了客户端希望接受的数据类型(如JSON、XML等)。在我们的例子中,虽然请求没有明确指定Accept头,Spring Boot会默认将返回值序列化为JSON格式。因此,问题很可能出在返回数据类型的格式化上。

我们在该请求的返回对象中,使用了自定义的Result类,用于封装返回的状态码、消息及数据内容,其结构大致如下:

public class Result {
    private Integer code;
    private String msg;
    private Object data;
}

通过Result类返回封装的信息,有助于我们在接口中统一返回格式。Result类中的code表示状态码,message包含提示信息,data字段存放返回的数据对象。然而,我们没有为Result类的字段添加getter和setter方法。

在Spring Boot中,使用@RestController注解的控制器方法会默认尝试将返回对象转换为JSON格式。如果Result类缺少getter和setter方法,Spring Boot将无法读取Result的属性进行JSON序列化,从而引发406 Not Acceptable错误。

解决方法

添加getter和setter方法,或添加@Data注解:

@Data
public class Result {
    private Integer code;
    private String msg;
    private Object data;
}

通过添加getter和setter方法,Jackson可以正确地读取和写入Result对象中的字段,从而将其转换为JSON格式返回给客户端。

接口测试

完成上述代码修改后,再次使用POST方式调用http://localhost:8080/depts,此时返回的数据应为JSON格式:

{
    "code": 1,
    "msg": "success",
    "data": [
        {
            "id": 1104,
            "name": "生产部",
            "createdTime": null,
            "updatedTime": null
        },
        {
            "id": 1110,
            "name": "工程部",
            "createdTime": null,
            "updatedTime": null
        },
        {
            "id": 1103,
            "name": "公关部",
            "createdTime": null,
            "updatedTime": null
        },
        {
            "id": 1107,
            "name": "生产部",
            "createdTime": null,
            "updatedTime": null
        },
        {
            "id": 1105,
            "name": "法律部",
            "createdTime": null,
            "updatedTime": null
        },
        {
            "id": 1102,
            "name": "会计及金融部",
            "createdTime": null,
            "updatedTime": null
        },
        {
            "id": 1108,
            "name": "研究及开发部",
            "createdTime": null,
            "updatedTime": null
        },
        {
            "id": 1106,
            "name": "市场部",
            "createdTime": null,
            "updatedTime": null
        },
        {
            "id": 1109,
            "name": "产品质量部",
            "createdTime": null,
            "updatedTime": null
        },
        {
            "id": 1101,
            "name": "工程部",
            "createdTime": null,
            "updatedTime": null
        }
    ]
}

原理探讨

Spring Boot中,@RestController注解标识的控制器方法默认返回JSON数据,这依赖于Spring的消息转换器(HttpMessageConverters)。Spring Boot内置了Jackson库作为JSON的默认转换工具。若返回的对象不具备getter和setter方法,Jackson将无法访问其属性,导致序列化失败,从而引发406 Not Acceptable异常。

在设计API返回对象时,建议始终遵循JavaBean的规范,为属性添加getter和setter方法,并确保字段可访问。这样不仅可以提高程序的兼容性,还能更好地遵循RESTful API的设计规范,避免序列化问题。

常见问题&调优建议

除了返回对象缺少getter/setter方法外,还可能出现以下问题导致406 Not Acceptable异常:

请求头不匹配:确保客户端的Accept头和服务端返回的Content-Type匹配,如application/json。
序列化冲突:若返回对象包含复杂类型,建议将复杂对象转换为简单类型或DTO,以便于JSON转换。
注解配置问题:在某些特殊需求下,可以通过@ResponseBody、@RequestMapping(produces="application/json")等注解控制返回类型。
此外,为了提高系统的健壮性和API接口的一致性,建议在项目中引入统一的响应处理机制。可以创建一个全局异常处理类,捕获序列化问题或类型转换问题,确保返回友好的错误信息,避免错误暴露给客户端。


猿道
14 声望0 粉丝