AngularJS请求外部API获取Token返回400错误(unsupported_grant_type)
你的问题核心在于请求体中的参数分隔符使用了HTML实体&而非标准的&,导致外部API的OAuth认证中间件无法正确解析grant_type参数,从而抛出unsupported_grant_type错误。
问题分析
虽然你的本地登录代码也用了&并能成功,但这大概率是因为本地ASP.NET Web API的中间件自动兼容了HTML实体转义;而外部API的认证逻辑没有处理这种情况,严格遵循x-www-form-urlencoded格式要求——参数必须用&分隔,而非转义后的&。
对比Postman的请求体(标准格式:grant_type=password&username=xxx&password=xxx)和你AngularJS代码发送的请求体(grant_type=password&username=xxx&password=xxx),就能看到明显差异。
解决方案
方案1:直接修复参数拼接的分隔符
把手动拼接字符串中的&替换成普通的&:
var _transferPersoon = function (portal, data) { var externalAPI = ""; if (portal == "portal1") { externalAPI = "https://urltoportal/webapi/"; } else if (portal == "portal2") { externalAPI = "https://urltoportal/webapi/"; } else if (portal == "portal3") { externalAPI = "https://urltoportal/webapi/"; } else if (portal == "portal4") { externalAPI = "https://urltoportal/webapi/"; } else { externalAPI = serviceBase; } // 修复分隔符为&,而非& var data = ["grant_type=password&username=", "transferaccount", "&password=", "password"].join(''); return $http.post([externalAPI, "token"].join(''), data, { headers: { "Content-Type": "application/x-www-form-urlencoded" } }).then(function (response) { return response.data.access_token; }) }
注意:建议用
then()替代已废弃的success()/error()方法,AngularJS 1.6+推荐使用标准Promise语法。
方案2:使用AngularJS内置序列化工具(更可靠)
手动拼接字符串容易出错,推荐使用$httpParamSerializerJQLike来自动序列化参数对象,它会严格按照x-www-form-urlencoded格式生成请求体:
// 记得在服务中注入$httpParamSerializerJQLike var _transferPersoon = function (portal, data) { var externalAPI = ""; if (portal == "portal1") { externalAPI = "https://urltoportal/webapi/"; } else if (portal == "portal2") { externalAPI = "https://urltoportal/webapi/"; } else if (portal == "portal3") { externalAPI = "https://urltoportal/webapi/"; } else if (portal == "portal4") { externalAPI = "https://urltoportal/webapi/"; } else { externalAPI = serviceBase; } var tokenParams = { grant_type: "password", username: "transferaccount", password: "password" }; return $http.post([externalAPI, "token"].join(''), $httpParamSerializerJQLike(tokenParams), { headers: { "Content-Type": "application/x-www-form-urlencoded" } }).then(function (response) { return response.data.access_token; }) }
验证方法
修改后,打开浏览器开发者工具的Network面板,查看请求的Form Data或Request Payload,确认参数格式和Postman发送的完全一致,即可解决问题。
内容的提问来源于stack exchange,提问作者Berend Hulshof

