Axios调用Google OAuth2刷新令牌失败但Postman成功的问题排查
问题描述
正在使用Google OAuth2流程编写函数,通过数据库存储的refresh_token调用接口获取新的access_token。Postman调用接口可成功获取结果,但用Axios编写的代码调用始终失败。已确认测试用的refresh_token、client_id和client_secret在两种调用方式中完全一致。
Postman调用配置
采用POST请求调用https://oauth2.googleapis.com/token,请求头设置Content-Type: application/x-www-form-urlencoded,通过表单形式传递refresh_token、grant_type=refresh_token、client_id、client_secret四个参数。
代码片段
export const getNewAccessToken = async (refreshToken: string): Promise<string> => { const url = 'https://oauth2.googleapis.com/token'; const data = { refresh_token: refreshToken, grant_type: "refresh_token", client_id: process.env.GOOGLE_CLIENT_ID, client_secret: process.env.GOOGLE_CLIENT_SECRET, }; try { let res = await axios.post(url, data, { headers: { 'content-type': 'application/x-www-form-urlencoded' }, }).then(response => { debugger; }).catch(e => { // 总是进入这里的catch debugger; }); } catch (e) { debugger; } }
错误信息
调用触发400 Bad Request错误,返回内容为:
{error: 'unsupported_grant_type', error_description:'Invalid grant_type: '}
补充信息
Postman生成的curl命令:
curl --location --request POST 'https://oauth2.googleapis.com/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'refresh_token=[与代码中一致的refresh_token]' \ --data-urlencode 'client_id=[同上]' \ --data-urlencode 'client_secret=[同上]' \ --data-urlencode 'grant_type=refresh_token'
问题
- 是否忽略了什么明显的问题?
- 可以尝试哪些调试方法?
曾尝试查看错误对象e,但无法在ClientRequest对象中找到原始请求信息。
问题原因
你设置了Content-Type: application/x-www-form-urlencoded请求头,但Axios默认会把JavaScript对象序列化为JSON格式,而非表单编码格式。Google OAuth2接口无法解析JSON格式的参数,导致实际未正确传递grant_type参数,因此返回unsupported_grant_type错误。
解决方法
有两种方式可以修正参数编码问题:
使用URLSearchParams格式化参数
将data对象转换为URLSearchParams实例,Axios会自动处理表单编码:const data = new URLSearchParams({ refresh_token: refreshToken, grant_type: "refresh_token", client_id: process.env.GOOGLE_CLIENT_ID, client_secret: process.env.GOOGLE_CLIENT_SECRET, });使用qs库序列化参数
先安装qs依赖:npm install qs然后在代码中用qs序列化参数:
import qs from 'qs'; // ... const data = qs.stringify({ refresh_token: refreshToken, grant_type: "refresh_token", client_id: process.env.GOOGLE_CLIENT_ID, client_secret: process.env.GOOGLE_CLIENT_SECRET, });
调试方法
- 打印实际请求内容:添加Axios请求拦截器,输出请求体和请求头,确认参数格式是否正确:
axios.interceptors.request.use(config => { console.log('请求体:', config.data); console.log('请求头:', config.headers); return config; }); - 对比curl与Axios请求:将Axios发送的参数格式和curl的
--data-urlencode格式对比,确认参数编码是否一致。 - 直接用curl验证参数:把代码中的参数替换到curl命令中执行,验证参数本身的有效性。
内容的提问来源于stack exchange,提问作者Allen Y

