Http请求报错406: “Not Acceptable”,

​ 接口在返回结果集的时候出现了406的报错。避坑。 - -!

一、报错信息内容:

后台接口提示信息

2023-11-23 09:44:04.062 WARN 15612 — [nio-8888-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation]

Postman调用接口报错信息

二、406状态码基本概念

​ 406 Not Acceptable是一个HTTP响应状态码,指示服务器无法实现客户端的一个 Accept-标头的请求响应。这通常是用户代理(即浏览器)指定一个可接受的字符集(通过Accept-Charset)、语言(通过Accept-Language)等应响应的结果,并且服务器无法提供此类响应。

​ 406:HTTP协议状态码的一种(4xx表示客户端的问题),表示客户端无法解析服务端返回的内容。就是后台的返回结果前台无法解析就会报406错误。

​ 406 Not Acceptable 表示用户代理(在大多数情况下是 Web 浏览器)请求了有效的资源,但请求包含一个特殊的 Accept- 标头,该标头向服务器指示有效响应只能包含特定类型的信息。下面是此类场景的几个栗子:

用户代理可能本地化为服务器无法提供的特定区域设置或语言。例如,用户代理可以使用 Accept-Language 请求标头来指定有效的法语语言(Accept-Language:fr),但如果服务器无法使用法语提供响应,则 406 代码可能是唯一正确的响应。用户代理可能请求服务器返回特定类型的内容。这些内容类型通常称为 MIME 类型,用于定义如纯文本(text/plain)、PNG 图像(image/png)、mp4 视频(video/mp4)等内容。因此,客户端可以在请求中包含 Accept 标头,并定义应由服务器提供的显式 MIME 类型(例如,Accept:application/xml)。如果服务器无法响应请求的匹配内容类型,则可能需要 406 Not Acceptable 响应。

在 HTTP 请求中可以提供少量其他 Accept- 标头,但绝大多数场景与上述类似:用户代理需要显式类型的响应,服务器要么提供响应,要么返回 406 代码以指示它无法实现请求。

三、代码原文:

统一返回值类的封装:

package cn.example.springbootproject.tempTestVue;

import lombok.Data;

import lombok.experimental.Accessors;

import java.util.HashMap;

import java.util.Map;

/**

* @Description: 实现统一返回数据

* @ClassName: R

*/

/** chain:支持链式编程 fluent:忽略get/set前缀 */

@Data

@Accessors(chain = true,fluent = true)

public class R {

private Boolean success;

private Boolean error;

/**响应提示信息*/

private String message;

/**相应状态码*/

private Integer code;

/**保存返回数据的对象*/

private Map data = new HashMap();

/**请求成功*/

public static R ok(){

R r = new R();

r.success(true);

r.message(ResultCodeEnum.SUCCESS.getMessage());

r.code(ResultCodeEnum.SUCCESS.getCode());

return r;

}

/**请求失败*/

public static R error(){

R r = new R();

r.error(false);

r.code(ResultCodeEnum.FAIL.getCode());

r.message(ResultCodeEnum.FAIL.getMessage());

return r;

}

/**返回数据对象赋值接口*/

public R data(String key,Object value){

this.data.put(key,value);

return this;

}

public R data(Map map){

this.data(map);

return this;

}

@Override

public String toString() {

return "R{" +

"success=" + success +

", error=" + error +

", message='" + message + '\'' +

", code=" + code +

", data=" + data +

'}';

}

}

​ 注: 这里我使用到了 @Accessors(chain = true,fluent = true) 这个注解,后续问题就出现在这个注解上

controller层代码:

package cn.example.springbootproject.tempTestVue;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.Map;

/**

* @Description: 雇员查询控制层

* @ClassName: EmplyeeController

*/

@RequestMapping("/employee")

@RestController

//@CrossOrigin

public class EmplyeeController {

@Autowired

HttpServletResponse response;

@GetMapping("queryList")

@ResponseBody

public R queryList(){

//这里就简单的封装一个模拟数据了,不在调用Service层在进行数据的查询

ArrayList employeeList = new ArrayList<>();

for (int i = 0; i < 5 ;i++) {

HashMap employee = new HashMap<>();

employee.put("id",i);

employee.put("name","张三"+i);

employee.put("age",20+i);

employee.put("sex","男");

employee.put("address","北京市朝阳区"+i);

employee.put("salary",10000+i);

employeeList.add(employee);

}

System.out.println("访问接口成功:employeeList = " + employeeList);

return R.ok().data("list",employeeList);

}

}

四、解决过程与方法

​ 基于这个问题,我在网上查询了许多解决方案,但是最终都未能解决我的这个问题…

这里先记录一下收集到的解决方案和尝试过程。

4.1 lombok注解

将返回的数据类型类中为所有属性添加get/set方法,或者添加@Data注解,或者添加@Setter和@Getter

​ 这里我的统一返回值类中,其实原本就打上了 @Data 注解(内部包含@Getter 和 @Setter 注解),这里在给统一返回类打上了@Getter 和 @Setter 注解尝试着能不能解决。

​ 结果一样,还是会出现同样的 406 错误! 下面就尝试着 第二种解决方案:

4.2 设置响应格式Json

​ 方式二的尝试是通过响应格式的设置,这里猜测是响应格式导致的问题,所以又尝试通过下面两种方式来解决这个问题:

在 @RequestMapping 注解上进行调整:

​ 这里将 Controller 层访问接口方法上的 @GetMapping注解进行了设置修改为:

@RequestMapping(value = “/queryList”,method = RequestMethod.GET,produces = “applications/json;charset=utf-8”)

​ 这里设置了响应格式 application/json;charset=utf-8 ,但是最后测试结果还是一样 406 没改变。

​ 于是在尝试着通过 responseBean 来设置 响应数据格式

通过Response.setContentType() 设置响应格式: ​ 哈哈哈,通过我注释掉就能知道,效果一样,还是 406 。 上面两种方式都是为了改变响应头,响应格式的修改,但是都是不起效,说明问题也不是出在这里。

4.3 406引发原因

​ 通过一系列的排查测试,最终找到 报错的原因了:

​ 导致页面报错 406: “Not Acceptable” 的原因就是 统一响应数据类 上的 @Accessors 注解中的 fluent=true属性。

​ 将 @Accessors 注解中的 fluent 属性删除掉之后在测试,响应结果就正常了,目前我所了解到的该注解的作用就是在我们调用实体类的 Get/Set 方法时,直接可以忽略掉get/set开通,就类似没有该属性时 setName(xxx),存在该属性值后只需要 object.name(xxx)。

统一返回类修改后的代码:

package cn.example.springbootproject.tempTestVue;

import lombok.Data;

import lombok.Getter;

import lombok.Setter;

import lombok.experimental.Accessors;

import java.util.HashMap;

import java.util.Map;

/**

* @Description: 实现统一返回数据

* @ClassName: R

*/

/** chain:支持链式编程 fluent:忽略get/set前缀 */

@Data

@Accessors(chain = true/*,fluent = true*/)

public class R {

private Boolean success;

private Boolean error;

/**响应提示信息*/

private String message;

/**相应状态码*/

private Integer code;

/**保存返回数据的对象*/

private Map data = new HashMap();

/**请求成功*/

public static R ok(){

R r = new R();

r.setSuccess(true);

r.setMessage(ResultCodeEnum.SUCCESS.getMessage());

r.setCode(ResultCodeEnum.SUCCESS.getCode());

return r;

}

/**请求失败*/

public static R error(){

R r = new R();

r.setError(false);

r.setCode(ResultCodeEnum.FAIL.getCode());

r.setMessage(ResultCodeEnum.FAIL.getMessage());

return r;

}

/**返回数据对象赋值接口*/

public R data(String key,Object value){

this.data.put(key,value);

return this;

}

public R data(Map map){

this.data(map);

return this;

}

@Override

public String toString() {

return "R{" +

"success=" + success +

", error=" + error +

", message='" + message + '\'' +

", code=" + code +

", data=" + data +

'}';

}

}

修改之后,调用接口,响应正常: ​ 问题解决了,下面就来了解一下关于这个 Lombok 提供的 @Accessors 注解的作用。 ​

五、@Accessors注解

​ Accessors注解是 lombok 插件包中的一个注解,中文含义是存取器。

打开Accessors 的源码观察:

5.1 fluent 属性

​ 如果属性 fluent = true :则访问器将以字段命名,即我们的 Get / Set 方法将不在包含get/set前缀,在调用时,是直接使用字段的名称即可。

如上图所示效果,我们在调用get / set方法时 ,就直接只用属性名称即可,不在添加 set /get 前缀!

5.2 chain属性

​ **默认为false(注:但是当fluent为true时,其默认为true),**生成的setter方法是void类型;如果设置为true生成的setter方法返回this(当前对象)。

​ 这就意味着,我们可以通过 set方式对操作对象进行链式编程了,即在调用set方法之后,还是返回其对象,然后就能直接进行下一步对象的操作,从而实现链式编程。

5.3 prefix属性

​ 可以指定前缀,生成getter和setter方法时会去掉指定的前缀(遵守驼峰命名)。

​ 相当于字符串截取功能,在生成getter和setter方法的时候,会自动截取去除指定前缀,然后加上get与set。

​ 请注意,仅当前一个字符不是小写字符或前缀的最后一个字母不是字母(例如下划线)时,前缀才算数。如果在去除前缀时多个字段都变成同一个名称,则会生成错误。

5.4 makeFinal属性

​ 如果为 true,则生成的访问器将被标记为 final。 默认值:false

​ 返回:是否应将访问器标记为 final。

文章链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。