【问题标题】:Insightly CRM Ajax API proxyInsightly CRM Ajax API 代理
【发布时间】:2015-11-23 03:50:18
【问题描述】:

更新

您好,我目前在尝试连接到 Insightlys CRM 的 API 时遇到了 CORS 错误。我正在通过 POSTMAN 成功检索我的 Insightly CRM 数据。

Insightly 不支持 AJAX 请求的 CORS (http://support.insight.ly/hc/en-us/community/posts/206961417-Using-the-API-with-AJAX),您必须设置 API 代理脚本。我使用了以下提供的在 python 中完成的代理脚本并上传到我的专用主机。但我仍然收到 401 错误?

我对 API 代理很陌生,所以不确定我是否在这里遗漏了什么?

获取 https://api.insight.ly/v2.2/Contacts 401(未经授权)

apiproxy.py

#
# Insightly API Proxy for Python / App Engine
# Brian McConnell <brian@insightly.com>
#
# This utility is designed to act as an API proxy server. It is written in  Python 2.7
# and will run without modification on Google App Engine. Just serve your JavaScript
# from the /js directory in this project, and AJAX requests should be relayed through
# to the Insightly service. This should also run on any webapp2 compatible server platform
#

import base64
import os
import string
import types
import urllib
import urllib2
import webapp2
import wsgiref.handlers

#
# import google app engine libraries, replace these with standard 
#

try:
    from google.appengine.ext import webapp
except:
# skip Google App Engine webapp library
    pass

base_url = 'https://api.insight.ly/v2.2'

def authenticate():
#
# add user validation logic here, be careful to do this in a way that does not expose user secrets such as
# the user API key (this is why we do not allow CORS in the first place)
#

# hardcoding API key for now

apikey = 'fillinhere'
return base64.encode(apikey)

def generateRequest(url, method, data, alt_auth=None, test=False, headers=None):
"""
This method is used by other helper functions to generate HTTPS requests and parse server responses. This will minimize the amount of work developers need to do to integrate with the Insightly API, and will also eliminate common sources of errors such as authentication issues and malformed requests. Uses the urllib2 standard library, so it is not dependent on third party libraries like Requests
"""
if type(url) is not str: raise Exception('url must be a string')
if type(method) is not str: raise Exception('method must be a string')
valid_method = False
response = None
text = ''
if method == 'GET' or method == 'PUT' or method == 'DELETE' or method == 'POST':
valid_method = True
else:
raise Exception('parameter method must be GET|DELETE|PUT|UPDATE')
request = urllib2.Request(url)
request.get_method = lambda: method
request.add_header('Content-Type', 'application/json')
if headers is not None:
headerkeys = headers.keys()
for h in headerkeys:
    request.add_header(h, headers[h])
# open the URL, if an error code is returned it should raise an exception
if method == 'PUT' or method == 'POST':
result = urllib2.urlopen(request, data)
else:
result = urllib2.urlopen(request)
text = result.read()
return text

class APIProxyHandler(webapp2.RequestHandler):

def delete(self):
    apikey = authenticate()
    path_qs = self.request.headers['path_qs']
    url = base_url + path_qs
    headers = {'Authorization','Basic ' + api_key}
    text = generateRequest(url, 'DELETE', None, headers=headers)
    self.response.headers['Content-Type']='application/json'
    self.response.out.write(text)
def get(self):
    apikey = authenticate()
    path_qs = self.request.headers['path_qs']
    url = base_url + path_qs
    headers = {'Authorization','Basic ' + api_key}
    text = generateRequest(url, 'GET', None, headers=headers)
    self.response.headers['Content-Type']='application/json'
    self.response.out.write(text)
def post(self):
    body = self.request.headers['body']
    path_qs = self.request.headers['path_qs']
    url = base_url + path_qs
    headers = {'Authorization','Basic ' + api_key}
    text = generateRequest(url, 'POST', body, headers=headers)
    self.response.headers['Content-Type']='application/json'
    self.response.out.write(text)
def put(self):
    body = self.request.headers['body']
    path_qs = self.request.headers['path_qs']
    url = base_url + path_qs
    headers = {'Authorization','Basic ' + api_key}
    text = generateRequest(url, 'PUT', body, headers=headers)
    self.response.headers['Content-Type']='application/json'
    self.response.out.write(text)

app = webapp2.WSGIApplication([("r/(.*)", APIProxyHandler)], debug=True)
# map generic page server request handler

jQuery/Ajax 调用:

Insightly = function () {
var _t = this;

this.events = function () {
    $(".contactsform").submit(_t.searchContacts);
};
this.searchContacts = function (event) {
event.preventDefault();
var $search = $(this);
console.log('[ Search contacts event ]');

$.ajaxPrefilter( function( options ) {
    if ( options.crossDomain ) {
    var newData = {};

    newData.data = $.extend({}, options.data);
    newData.url = options.url;

    options = {};

    // Set the proxy URL
    options.url = "http://www.blahblah.comm/apiproxy.py";
    console.log("ajaxprefilter");
    options.data = $.param(newData);
    console.log(options);
    options.crossDomain = false;
}
});
// How to use the cross domain proxy
$.ajax({         
    url: 'https://api.insight.ly/v2.2/Contacts',
    crossDomain: true, 
    processData: false 
    }).success(function(data) { 
    var jsonData = JSON.parse(data); 
    console.log(jsonData); 
});
};//End SearchContacts
this.init = function () {
    _t.events();
};
this.init();
return (this);   
};//End main function
var LZ = new Insightly();

非常感谢您的帮助。

【问题讨论】:

  • 他们的内容类型是否支持 JSONP(带填充的 Json)?您需要将响应视为 javascript。这通常可以解决 CORS 问题。
  • 您好,感谢您的回复。我无法在他们的文档 ref jsonp 中找到任何内容。但是我确实尝试将 dataType 设置为 jsonp 并收到 GET 401 错误消息?

标签: jquery python ajax cors crm


【解决方案1】:

这意味着您不能使用来自客户端的 AJAX 或 javascript http 请求直接访问他们的 API。您需要做的是创建一个“代理”API。

“代理”API 是您需要在客户端代码所在的域中托管的服务。您需要使用您最喜欢的服务器端语言编写一个经过身份验证的服务器端 API 调用以调用 Insightly CRM API,并使用您的 jquery AJAX 调用它。

所以下面的代码将是:

var searchNotesUrl = "https://myowndomain.com/your/api/proxy/notes"

换句话说,您的服务器端脚本会将数据从 CRM API 中继到您的客户端。

如果仍然收到 401 响应,那么您可能需要检查用户和 API 密钥凭据是否正确。

更新

你现在完全改变了问题。我上面的答案是针对您的最后一个问题版本。

对于新答案,您对此代码有疑问:

$.ajaxPrefilter( function( options ) {
    if ( options.crossDomain ) {
    var newData = {};

    newData.data = $.extend({}, options.data);
    newData.url = options.url;

    options = {};

    // Set the proxy URL
    options.url = "http://www.blahblah.comm/apiproxy.py";
    console.log("ajaxprefilter");
    options.data = $.param(newData);
    console.log(options);
    options.crossDomain = false;
}
});
// How to use the cross domain proxy
$.ajax({         
    url: 'https://api.insight.ly/v2.2/Contacts',
    crossDomain: true, 
    processData: false 
    }).success(function(data) { 
    var jsonData = JSON.parse(data); 
    console.log(jsonData); 
});

首先,您仍在请求'https://api.insight.ly/v2.2/Contacts'。您不再需要请求此 URL,因为您已经拥有代理 api。代理 api ("http://www.blahblah.comm/apiproxy.py") 是您应该直接调用的,而不是向insight.ly 发送请求。

您的代理 API 应处理可以在insight.ly 中发送的所有 api 请求。也就是说"http://www.blahblah.comm/apiproxy.py"可以支持notes、Contacts等。

其次,您可以阅读 python 脚本,它实际上会查找请求标头键 'path_qs' 。 path_qs 值应该类似于 /Contacts。原因是因为path_qs 在脚本中通过url = base_url + path_qs 与base_url = 'https://api.insight.ly/v2.2' 连接。

所以代码是这样的:

$.ajax({ 
   method: 'GET', //this is because it is supposed to be GET
   crossDomain: true,
   dataType: 'json',
   url: 'http://www.blahblah.comm/apiproxy/that/executes', 
   headers: { 'path_qs': '/Contacts' },
   success : function(data, status, xhr) {
      console.log(data);
   }
});

您需要将 Google 应用引擎云中的 Python 脚本设置为应用程序。要了解更多信息,这是一个全新的主题: https://cloud.google.com/appengine/docs/python/gettingstartedpython27/helloworld

【讨论】:

  • 您好,感谢您的回复。 Insightly 技术人员说了同样的话,并为我提供了一个仅适用于 Google App Engine 并具有 webapp2 的 python 代理脚本,但我想在我现有的专用主机上使用它(因为成本),因此需要修改。我是 API 代理脚本的新手,我相信您也可以使用 Nodejs/expressjs 来做到这一点?脚本需要捕获 Accept、Content-Type 和 Authorization 头
  • @RRowan 是的,nodejs/express 可以使用 http 模块做代理脚本。我为你找到了一些 sn-ps / 教程:docs.nodejitsu.com/articles/HTTP/clients/…
  • 您好文森特,感谢您的帮助。我尝试使用技术支持提供的 python 脚本github.com/Insightly/insightly-python/blob/v2.2/apiproxy.py 并更新了我上面的代码/帖子,但仍然收到 401 消息。我相信凭据是正确的。 Webhost 表示他们也支持 webapp2 和 python。
  • @RRowan 您的客户端代码与您的 py 脚本托管在同一台服务器上吗?如果您使用的是 chrome,实际上您可以先使用 POSTMAN(一个 chrome 扩展)测试您的 python 服务,以验证您的代码是否正常工作,然后您可以将其集成到您的 javascript 中。它可以测试您的 HTTP 响应。这是一个很好的做法,首先在那里进行测试以隔离问题。
  • 感谢您的快速回复。我在第一个实例中成功地从 POSTMAN 中检索到数据/信息,直接使用 api key/basic auth 连接到有洞察力的 API。但是你的意思是通过邮递员连接/测试到 apiproxy.py 脚本吗?如果是这样,最好的测试方法是什么?我在本地工作,我的 apiproxy.py 在服务器上
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-25
  • 2011-01-14
相关资源
最近更新 更多