最近使用 axios 和 go-restful 分别作为前后端 API 开发时,出了个奇怪的 406 问题,记录下来,以备后需。
我们的项目前端使用 vue 和 element-ui 搭建,后台使用 go-restful 来编写 API。以前开发基本顺利,没有出现过奇怪的问题,但是最近突然出现了个莫名其妙的 406 问题,查阅文档,各种尝试,始终没有找到优雅的解决方法,导致开发进度被推迟了两天。目前,该问题已经解决,特此记录。
前端使用 axios 编写的接口如下:(src/api/kubernetes/deployment.js)
|
|
其中,axios 实例的定义为:(src/utils/kubernetesRequest.js)
|
|
然后在组件(src/views/kubernetes/application/deployment/index.vue)中调用该方法
<script>
import { restartApp } from '@/api/kubernetes/deployment';
import { setCurrentCluster, setCurrentApplication } from '@/utils/auth';
import { handleScaleError } from '@/utils/utils';
import { mapGetters } from 'vuex';
export default {
name: 'Deployment',
data() {
return {
apps: [],
currentApp: '',
temp: {
app: {},
revision: '',
},
};
},
computed: {
...mapGetters([
'currentGroup',
'currentCluster',
'currentApplication'
])
},
created() {
},
methods: {
handleRestart(index, app) {
this.$confirm(this.$t('confirm.restart'), this.$t('confirm.title'), {
confirmButtonText: this.$t('confirm.confirm'),
cancelButtonText: this.$t('confirm.cancel'),
type: 'warning'
}).then(() => {
restartApp(this.currentCluster.id, app.namespace, app.name).then(response => {
if (response.status === 201) {
this.$message({
type: 'success',
message: this.$t('notification.restartSuccessMessage'),
duration: 2000,
offset: 40
});
}
this.getAppsData();
}).catch(error => {
if (error.response) {
if (error.response.status === 403) {
this.$message({
type: 'error',
message: error.message,
duration: 2000,
offset: 40
});
} else {
this.$message({
message: this.$t('notification.restartFailedMessage'),
type: 'error',
duration: 2000,
offset: 40
});
}
} else {
this.$message({
type: 'error',
message: error.message,
duration: 2000,
offset: 40
});
}
});
}).catch(() => {
this.$message({
type: 'info',
message: this.$t('confirm.cancelRestart'),
duration: 2000,
offset: 40
});
});
}
};
</script>
点击重启按钮时,传入当前行的值作为 app 的值,请求后台 API。
后台使用 go-restful 开发的 API,主要代码为:
|
|
使用 PUT 方法向后台 API 发送请求后,返回的结果为:
|
|
查询 MDN 文档,对 406 解释如下:
HTTP 协议中的 406 Not Acceptable 状态码表示客户端错误,指代服务器端无法提供与 Accept-Charset
以及 Accept-Language
消息头指定的值相匹配的响应。
在实际应用中,这个错误状态码极少使用:不是给用户返回一个晦涩难懂(且难以更正)的错误状态码,而是将相关的消息头忽略,同时给用户提供一个看得见摸得着的页面。这种做法基于这样一个假设:即便是不能达到用户十分满意,也强于返回错误状态码。
如果服务器返回了这个错误状态码,那么消息体中应该包含所能提供的资源表现形式的列表,允许用户手动进行选择。
根据文档的提示,我检查了 axios 请求实例的 Accept-Charset
和 Accept-Language
消息头的设置。实际上,我并没有显式指定这两个请求头的值,那么在默认情况下,浏览器和服务器之间协商的结果应该是一致的,所以应该不是这两个请求头不一致导致的。
由于我们使用了 envoy 来做代理,我就搜索了 envoy 相关的资料,envoy 官方网站对 响应状态码 的解释如下:
Code | Name | Description |
---|---|---|
406 | Not Acceptable | The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request. |
也就是说,请求的资源只能根据发送的请求头 Accept 生成不可接受的内容。根据此提示,我注意到 响应头中的 Accept
为 text/plain
,而请求头中的 accept
为 application/json
,所以,应该是由 envoy 返回的 406 错误。
但是另外的代码使用 PUT 方法发送请求却没有这个问题,于是,我就尝试着改造了一下代码,最终发现,由于我的请求数据全部在请求头和路径参数中,所以请求体没有设置,当我把请求体设置上后,返回结果就正常了,虽然请求体的内容为空。改造后的前端 API 如下所示:
|
|
整个请求和响应就正常了。
参考资料
https://developers.envoy.com/#response-codes
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status/406