Http請求報錯406: “Not Acceptable”,
? 接口在返回結果集的時候出現(xiàn)了406的報錯。避坑。 - -!
一、報錯信息內(nèi)容:
- 后臺接口提示信息
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狀態(tài)碼基本概念
? 406 Not Acceptable是一個HTTP響應狀態(tài)碼,指示服務器無法實現(xiàn)客戶端的一個 Accept-標頭的請求響應。這通常是用戶代理(即瀏覽器)指定一個可接受的字符集(通過Accept-Charset)、語言(通過Accept-Language)等應響應的結果,并且服務器無法提供此類響應。
? 406:HTTP協(xié)議狀態(tài)碼的一種(4xx表示客戶端的問題),表示客戶端無法解析服務端返回的內(nèi)容。就是后臺的返回結果前臺無法解析就會報406錯誤。
? 406 Not Acceptable 表示用戶代理(在大多數(shù)情況下是 Web 瀏覽器)請求了有效的資源,但請求包含一個特殊的 Accept- 標頭,該標頭向服務器指示有效響應只能包含特定類型的信息。下面是此類場景的幾個栗子:
- 用戶代理可能本地化為服務器無法提供的特定區(qū)域設置或語言。例如,用戶代理可以使用 Accept-Language 請求標頭來指定有效的法語語言(Accept-Language:fr),但如果服務器無法使用法語提供響應,則 406 代碼可能是唯一正確的響應。
- 用戶代理可能請求服務器返回特定類型的內(nèi)容。這些內(nèi)容類型通常稱為 MIME 類型,用于定義如純文本(text/plain)、PNG 圖像(image/png)、mp4 視頻(video/mp4)等內(nèi)容。因此,客戶端可以在請求中包含 Accept 標頭,并定義應由服務器提供的顯式 MIME 類型(例如,Accept:application/xml)。如果服務器無法響應請求的匹配內(nèi)容類型,則可能需要 406 Not Acceptable 響應。
在 HTTP 請求中可以提供少量其他 Accept- 標頭,但絕大多數(shù)場景與上述類似:用戶代理需要顯式類型的響應,服務器要么提供響應,要么返回 406 代碼以指示它無法實現(xiàn)請求。
三、代碼原文:
- 統(tǒng)一返回值類的封裝:
package cn.example.springbootproject.tempTestVue;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.HashMap;
import java.util.Map;
/**
* @Description: 實現(xiàn)統(tǒng)一返回數(shù)據(jù)
* @ClassName: R
*/
/** chain:支持鏈式編程 fluent:忽略get/set前綴 */
@Data
@Accessors(chain = true,fluent = true)
public class R {
private Boolean success;
private Boolean error;
/**響應提示信息*/
private String message;
/**相應狀態(tài)碼*/
private Integer code;
/**保存返回數(shù)據(jù)的對象*/
private Map data = new HashMap<String,Object>();
/**請求成功*/
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;
}
/**返回數(shù)據(jù)對象賦值接口*/
public R data(String key,Object value){
this.data.put(key,value);
return this;
}
public R data(Map<String,Object> 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) 這個注解,后續(xù)問題就出現(xiàn)在這個注解上
- 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(){
//這里就簡單的封裝一個模擬數(shù)據(jù)了,不在調用Service層在進行數(shù)據(jù)的查詢
ArrayList<Map> employeeList = new ArrayList<>();
for (int i = 0; i < 5 ;i++) {
HashMap<String, Object> employee = new HashMap<>();
employee.put("id",i);
employee.put("name","張三"+i);
employee.put("age",20+i);
employee.put("sex","男");
employee.put("address","北京市朝陽區(qū)"+i);
employee.put("salary",10000+i);
employeeList.add(employee);
}
System.out.println("訪問接口成功:employeeList = " + employeeList);
return R.ok().data("list",employeeList);
}
}
四、解決過程與方法
? 基于這個問題,我在網(wǎng)上查詢了許多解決方案,但是最終都未能解決我的這個問題…
這里先記錄一下收集到的解決方案和嘗試過程。
4.1 lombok注解
將返回的數(shù)據(jù)類型類中為所有屬性添加get/set方法,或者添加@Data注解,或者添加@Setter和@Getter
? 這里我的統(tǒng)一返回值類中,其實原本就打上了 @Data 注解(內(nèi)部包含@Getter 和 @Setter 注解),這里在給統(tǒng)一返回類打上了@Getter 和 @Setter 注解嘗試著能不能解決。
? 結果一樣,還是會出現(xiàn)同樣的 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 來設置 響應數(shù)據(jù)格式
-
通過Response.setContentType() 設置響應格式:
? 哈哈哈,通過我注釋掉就能知道,效果一樣,還是 406 。
上面兩種方式都是為了改變響應頭,響應格式的修改,但是都是不起效,說明問題也不是出在這里。
4.3 406引發(fā)原因
? 通過一系列的排查測試,最終找到 報錯的原因了:
? 導致頁面報錯 406: “Not Acceptable” 的原因就是 統(tǒng)一響應數(shù)據(jù)類 上的 @Accessors 注解中的 fluent=true屬性。
? 將 @Accessors 注解中的 fluent 屬性刪除掉之后在測試,響應結果就正常了,目前我所了解到的該注解的作用就是在我們調用實體類的 Get/Set 方法時,直接可以忽略掉get/set開通,就類似沒有該屬性時 setName(xxx),存在該屬性值后只需要 object.name(xxx)。
- 統(tǒng)一返回類修改后的代碼:
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: 實現(xiàn)統(tǒng)一返回數(shù)據(jù)
* @ClassName: R
*/
/** chain:支持鏈式編程 fluent:忽略get/set前綴 */
@Data
@Accessors(chain = true/*,fluent = true*/)
public class R {
private Boolean success;
private Boolean error;
/**響應提示信息*/
private String message;
/**相應狀態(tài)碼*/
private Integer code;
/**保存返回數(shù)據(jù)的對象*/
private Map data = new HashMap<String,Object>();
/**請求成功*/
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;
}
/**返回數(shù)據(jù)對象賦值接口*/
public R data(String key,Object value){
this.data.put(key,value);
return this;
}
public R data(Map<String,Object> 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方法之后,還是返回其對象,然后就能直接進行下一步對象的操作,從而實現(xiàn)鏈式編程。
5.3 prefix屬性
? 可以指定前綴,生成getter和setter方法時會去掉指定的前綴(遵守駝峰命名)。
? 相當于字符串截取功能,在生成getter和setter方法的時候,會自動截取去除指定前綴,然后加上get與set。
? 請注意,僅當前一個字符不是小寫字符或前綴的最后一個字母不是字母(例如下劃線)時,前綴才算數(shù)。如果在去除前綴時多個字段都變成同一個名稱,則會生成錯誤。
5.4 makeFinal屬性
? 如果為 true,則生成的訪問器將被標記為 final。 默認值:false文章來源:http://www.zghlxwxcb.cn/news/detail-846820.html
? 返回:是否應將訪問器標記為 final。文章來源地址http://www.zghlxwxcb.cn/news/detail-846820.html
到了這里,關于響應406報錯Resolved [HttpMediaTypeNotAcceptableException Could not find acceptable representation]的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!