NodeJS代理未按预期工作

3
当前请求的路由是从localhost:3001发起的,经过同一台主机上运行的localhost:3001/proxy代理,然后请求被路由到Salesforce实例。代理使用ExpressJS制作,客户端应用程序使用AngularJS制作。注意:我确保在我的密码末尾标记了安全令牌(Salesforce API要求),但是当使用cURL时,似乎不必这样做。以下是一系列HTTP跟踪,希望能提供一些线索: 来自Angular.JS应用程序的HTTP请求日志:
POST /proxy HTTP/1.1
Host: localhost:3001
Connection: keep-alive
Content-Length: 234
Pragma: no-cache
Cache-Control: no-cache
X-User-Agent: salesforce-toolkit-rest-javascript/v29.0
Origin: http://localhost:3001
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.107 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: application/json, text/plain, */*
SalesforceProxy-Endpoint: https://uniquename.salesforce.com/services/oauth2/token
Referer: http://localhost:3001/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Cookie: liveagent_oref=; liveagent_ptid=3c69c2f9-139d-4439-ba6c-fd8d9dcae101; liveagent_vc=5
grant_type=password&client_id=3MVGxyzxyzxyzxyz&client_secret=99889988&username=first.last%40email.com&password=pswdwACYodaYfHs

400 Bad Request
Object {error_description: "grant type not supported", error: "unsupported_grant_type"}

用于代理路由的相关Express.JS代码:

app.all('/proxy', function(req, res) {
    var url = req.header('SalesforceProxy-Endpoint');
    console.log(req.body); //prints all form data variables in JSON format
    console.log(res.body); //undefined

    request({url: url}).pipe(res).on('error', function(error){
        //I think I may need to pipe more information using request?
        console.log(error);
    });
});

使用cURL获取请求的详细信息:

curl -v https://uniquename.salesforce.com/services/oauth2/token
 -d "grant_type=password" -d "client_id=3MVGxyzxyzxyzxyz"
-d "client_secret=99889988" -d "username=jfirst.last@email.com" -d "password=pswd"

> POST /services/oauth2/token HTTP/1.1
> User-Agent: curl/7.41.0
> Host: uniquename.salesforce.com
> Accept: */*
> Content-Length: 207
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 207 out of 207 bytes
< HTTP/1.1 200 OK
< Date: Wed, 29 Jul 2015 06:04:55 GMT
< Set-Cookie: BrowserId=auu1mgvHSMS1EedDEduz8Q;Path=/;Domain=.salesforce.com;Exp
ires=Sun, 27-Sep-2015 06:04:55 GMT
< Expires: Thu, 01 Jan 1970 00:00:00 GMT
< Pragma: no-cache
< Cache-Control: no-cache, no-store
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
<
{
  "id":"https://test.salesforce.com/id/05390530530",
  "issued_at":"1438132525896197",
  "token_type":"Bearer",
  "instance_url":"https://uniquename.salesforce.com",
  "signature":"blahblah",
  "access_token":"XXXXXXXXXXXXXXXXX"
}
* Connection #0 to
host uniquename.salesforce.com left intact

如您所见,我从cURL请求中收到了有效的响应。 我怀疑代理存在问题,因为可能没有将所有表单数据转发到Salesforce,但我不知道如何在Express.JS中进行调试。 我怀疑的原因是,如果我尝试curl https://uniquename.salesforce.com/services/oauth2/token它会返回相同的unsupported_grant_type错误。

跟进:我尝试使用request.post(url).form({key:'value'})方法,这是提交URL编码表单的方式,但我仍然收到400响应。 - jlewkovich
看起来你的代理没有将 content-type 标头传递到终端。 - Christopher Johnson
我尝试了 request({url: url, headers: {'Content-Type': 'application/x-www-form-urlencoded'}}) 但它仍然不起作用...我应该以另一种方式传递它吗? - jlewkovich
在这里发布悬赏:http://salesforce.stackexchange.com/questions/85349/getting-unsupported-grant-type-error-when-using-angular-js-app-expressjs-pro。 - jlewkovich
2个回答

2

我最终通过切换使用 cors-anywhere 代理来使这个工作正常。我在端口8080上部署了我的AngularJS应用程序,代理在端口3001上运行。我使用 npmgrunt 管理我的包。以下是代理的代码:

var host = 'localhost';
var port = 3001;
var cors_proxy = require('cors-anywhere');
cors_proxy.createServer().listen(port, host, function() {
    console.log('CORS proxy running on ' + host + ':' + port);
});

下面是我在AngularJS中发起HTTP请求的方法(您需要在数据对象中填写自己的凭据):

var login = {
    method: 'POST',
    url: 'http://localhost:3001/https://login.salesforce.com/services/oauth2/token',
    data: 'grant_type=password&client_id='+encodeURIComponent(CLIENT_ID)+'&client_secret='+encodeURIComponent(CLIENT_SECRET)+'&username='+encodeURIComponent(EMAIL)+'&password='+encodeURIComponent(PASSWORD+USER_SECURITY_TOKEN),
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Accept': '*/*'
    }
};

$http(login).success(function(response){
    console.log(response);
})
.error( function(response, status){
    console.log(response);
    console.log("Status: " + status);
});

你可以使用命令node server.js运行代理,使用grunt运行AngularJS应用程序。希望这能帮助某些人解决问题,这是一个很棘手的问题。

0

我认为你没有代理HTTP请求中的所有内容。我在下面包含了一些代码,可以将请求方法和请求头传递到你的端点。另外,尝试使用'request-debug'库,这样你就可以比较代理HTTP请求和curl请求,并查找差异。

var express = require('express');
var app = express();
var request = require('request');
require('request-debug')(request);

app.all('/proxy', function(req, res) {
    var url = req.header('SalesforceProxy-Endpoint');

    var options = {
        url: url,
        method: req.method,
        headers: req.headers
    }

    request(options).pipe(res).on('error', function (error, response, body) {
        if (!error && response.statusCode == 200) {
            //TODO: do something useful with the response
        } else {
            console.log('ERROR: ' + error);
        }
    });
});

这个方法给了我HTTP错误 405 (Method Not Allowed)。而且,我不太确定如何获取代理的HTTP请求。那不是req对象相同的吗?如果是的话,那么我已经在原始帖子中发布了这些细节。 - jlewkovich
1
我已经解决了最初的错误,现在我从Salesforce收到了响应:HTTP/1.1 405 Method Not Allowed Allow: GET, HEAD, OPTIONS Date: Tue, 04 Aug 2015 21:58:53 GMT Connection: keep-alive Transfer-Encoding: chunked如何启用POST? - jlewkovich

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接