如何使用Coldfusion CFHTTP将JSON数据发布到远程API

gox*_*dia 13 coldfusion json cfhttp

我确信我完全搞砸了,但是在Stack Overflow用户的帮助下我得到了这么多,所以非常感谢到目前为止.

我需要将JSON数据POST到远程API.显然,由于SOP问题,我无法使用jQuery,而且远程API不支持JSONP.

我也不想使用任何类型的代理来解决SOP限制.

根据API文档(http://myemma.com/api-docs/),这是他们期望的数据格式(请求和响应数据作为JSON传输):

POST https://api.e2ma.net//123/members/add
{
  "fields": {
    "first_name": "myFirstName"
  }, 
  "email": "email@domain.com"
}
Run Code Online (Sandbox Code Playgroud)

这是我迄今为止构建的,但继续从远程API接收"无法解析JSON"错误:

<cfset fields[name_first]="#SerializeJSON( "myFirstName" )#" />
<cfset form.email="#SerializeJSON( "email@domain.com" )#" />

<cfhttp
  url="https://api.e2ma.net/123/members/add"
  method="POST"
  username="username"
  password="pssword"
  useragent="#CGI.http_user_agent#"
  result="objGet">

  <!--- add email --->
  <cfhttpparam
    type="formfield"
    name="email"
    value='#form.email#'
  />

  <!--- add field: name_first --->
  <cfhttpparam
    type="formfield"
    name="fields"
    value='#fields[name_first]#'
  />

</cfhttp>

<cfoutput>#objGet.FileContent#</cfoutput>
Run Code Online (Sandbox Code Playgroud)

同样,我肯定会以某种方式破坏我的数据结构,但我不确定我做错了什么,特别是关于正确设置"fields":{"first_name":"myFirstName"}结构/数组.

tim*_*own 23

您应该将请求字符串作为主体的httpparam类型发送.请求的主体可能类似于准备好的结构的整个表单范围.确保在隐式结构创建期间使用数组表示法来设置结构键或将它们放在"引号"中,以确保在serializeJSON()发生时它们保留正确的大小写,否则ColdFusion将大写结构键.

<cfset stFields = {
    "fields" = {
        "first_name" = "myFirstName"
     }, 
     "email" = "email@domain.com"
}>   

<cfhttp url="http://api.url.com" method="post" result="httpResp" timeout="60">
    <cfhttpparam type="header" name="Content-Type" value="application/json" />
    <cfhttpparam type="body" value="#serializeJSON(stFields)#">
</cfhttp>
Run Code Online (Sandbox Code Playgroud)

更新10/26/13
对于我最近使用API​​进行的所有工作,我认为我会更新一种简单的方法来自动化我发现的这种外壳.我使用了JSON Util库和Ben Nadel的JSON Serializer Utility CFC的组合来实现所有返回的更好的序列化一致性.

下面是我如何实现这个的示例GIST.
https://gist.github.com/timmaybrown/7226809

当我在我的项目中转换为使用持久性实体CFC时,我发现使用我自己的子CFC方法扩展Ben Nadel的序列化器CFC,该方法使用getComponentMetaData()函数循环所有持久性cfc的属性,以构建不同键的结构以及序列化的套管.该方法允许我的api自动继承我的实体中的属性名称的大小,非常有用.重新启动需要一些开销,但非常值得在API中保持套管一致.

更新9/8/16 回复:我的观点是关于一致的套管.我已经在我的数据库中针对新项目采用了不同的列命名约定,因此我不必为很多这些问题而斗争.first_name而不是firstName


Ste*_*ich 8

更新:2012年9月26日:在我使用我设置的模拟账户申请API密钥后,他们发送了一个以及may account_id.我将代码放在下面,它就像添加成员的魅力一样.

首先让我说这些代码都没有经过测试 (参见上面的更新).我没有MyEmma帐户,显然您必须是account_id的付费客户才能使用API​​.那吹!但这应该让你真正接近,并可能给你一些封装逻辑的想法,这已经成为我的痴迷.

其次,我意识到这篇文章已经有9个月了,你可能已经很久没想到了,或者赢了彩票并且现在正在运行这个地方.所以没有人可能会看到这篇文章.但是我一直在寻找一些答案,并且遇到了它...而且因为制定和解析JSON是我日常生活的一部分,所以我总是需要继续自我设定.所以结果是对你的问题的快速回答,成为一个深夜,自我服务,强迫性的挑战.好歹...

...你正在用JSON做什么,正在创建客户端嵌套结构.您具有两个键值对(字段和电子邮件)的根结构.然后,结构"字段"包含一个结构,其中包含您为该电子邮件地址(first_name)发送的键值对.想必你可以发送更多.

您正在构建嵌套结构.请记住,结构中的键可以包含结构.这些键可以容纳结构,等等.它可以变得像你想要的那样黑暗和讨厌.但是,所有JSON都是......它是客户端对象.

所以这是你的数据构建和JSON对象......

<cfscript>
    variables.dataFields = {};
    variables.dataFields['fields'] = {};
    variables.dataFields['email'] = "email@domain.com";
    variables.dataFields.fields['first_name'] = "myFirstName";
    variables.dataFields = serializejson(variables.dataFields);
</cfscript>
Run Code Online (Sandbox Code Playgroud)

请注意,我使用数组表示法显式设置结构键名.我们必须这样做来控制Coldfusion的情况.否则,密钥将全部封顶...不希望我们想要区分大小写的JavaScript.这可能是您遇到的问题的一部分.

如果艾玛因为案件而不理解,那么你会得到你的......

{"error": "Unable to parse JSON request"}
Run Code Online (Sandbox Code Playgroud)

但是当我们使用数组表示法显式设置我们的键名,然后序列化我们的对象时,我们会变得漂亮,漂亮,时尚的JSON ......

{"fields":{"first_name":"myFirstName"},"email":"email@domain.com"}
Run Code Online (Sandbox Code Playgroud)

下面,我将我们的http请求发送到一个函数中的Emma.将Content-Type标头设置为application/json也非常重要,因此浏览器会将其作为对象发送,而不仅仅是文本字符串.而且我们正在把我们的JSON作为我们的要求的身体,没有所谓的"领域"表单域...希望这是有道理的,当你大声说出来.这是功能......

<cffunction name="callEmma" access="private" displayname="CallEmma" description="This makes an HTTP REQUEST to MyEmma" returnformat="JSON" output="false" returntype="Any">
    <cfargument name="endpoint" required="true" type="string" displayname="EndPoint">
    <cfargument name="PUBLIC_API_KEY" required="true" type="string" displayname="PUBLIC_API_KEY">
    <cfargument name="PRIVATE_API_KEY" required="true" type="string" displayname="PRIVATE_API_KEY">
    <cfargument name="dataFields" required="true" type="struct" displayname="DataFields">
    <cfscript>
        local = {};
        local.baseURL = "https://api.e2ma.net/";
        local.account_id = "12345";
        local.phoneNumber = local.baseURL & local.account_id & arguments.endPoint;
        local.connection = new http();
        local.connection.setMethod("POST"); 
        local.connection.setUrl(local.phoneNumber);
        local.connection.setUsername(arguments.PUBLIC_API_KEY);
        local.connection.setPassword(arguments.PRIVATE_API_KEY);
        local.connection.setUserAgent(cgi.http_user_agent);
        local.connection.addParam(type="header",name="Content-Type", value="application/json");
        local.connection.addParam(type="body", value=arguments.dataFields); 
        local.objGet = local.connection.send().getPrefix();
        local.content = local.objGet.filecontent;
        return local.content
    </cfscript>
</cffunction>
Run Code Online (Sandbox Code Playgroud)

然后再一次,这是我们的JSON构建(嵌套结构)......

<cfscript>
    variables.dataFields = {};
    variables.dataFields['fields'] = {};
    variables.dataFields['email'] = "email@domain.com";
    variables.dataFields.fields['first_name'] = "myFirstName";
    variables.dataFields = serializejson(variables.dataFields);
</cfscript>
Run Code Online (Sandbox Code Playgroud)

然后我们设置变量传递给函数...

<cfscript>
    variables.entryPoint = "/members/add";
    variables.PUBLIC_API_KEY= "PUBLIC_API_KEY";
    variables.PRIVATE_API_KEY= "PRIVATE_API_KEY";
</cfscript>
Run Code Online (Sandbox Code Playgroud)

然后拨打电话......

<cfscript>
    variables.myResponse = callEmma(variables.entryPoint,variables.PUBLIC_API_KEY,variables.PRIVATE_API_KEY,variables.dataFields);
    variables.myResponse = deserializejson(variables.myResponse);
</cfscript>
Run Code Online (Sandbox Code Playgroud)

然后我们接受我们的响应,反序列化它,然后输出我们想要的变量.

<cfscript>
    if(variables.myResponse.added){
        writeoutput("Member " & variables.myResponse.member_id & " added!");
    }
    else{
        writeoutput("There was an error adding this member");
    }
</cfscript>
Run Code Online (Sandbox Code Playgroud)

我已经<cfscript>尽可能多地使用了.它更容易阅读,它让我感觉比我真的更聪明.所以当我们把它们放在一起时,为了切割和粘贴,我们有这个...

<cfscript>
// Function to make our calls to Emma
private any function callEmma(required string endPoint,required string PUBLIC_API_KEY,required string PRIVATE_API_KEY,required string dataFields)
    description="This makes an HTTP REQUEST to MyEmma"
    displayname="CallEmma"
    returnformat="JSON"
    output="false"
{
    local = {};
    local.baseURL = "https://api.e2ma.net/";
    local.account_id = "12345";
    local.phoneNumber = local.baseURL & local.account_id & arguments.endPoint;
    local.connection = new http();
    local.connection.setMethod("POST"); 
    local.connection.setUrl(local.phoneNumber);
    local.connection.setUsername(arguments.PUBLIC_API_KEY);
    local.connection.setPassword(arguments.PRIVATE_API_KEY);
    local.connection.setUserAgent(cgi.http_user_agent);
    local.connection.addParam(type="header",name="Content-Type", value="application/json");
    local.connection.addParam(type="body",value=arguments.dataFields); 
    local.objGet = local.connection.send().getPrefix();
    local.content = local.objGet.filecontent;
    return local.content;
} 

// Put our data together
variables.dataFields = {};
variables.dataFields['fields'] = {};
variables.dataFields['email'] = "email@domain.com";
variables.dataFields.fields['first_name'] = "myFirstName";
variables.dataFields = serializejson(variables.dataFields);

// Define the parameters for our call to Emma
variables.entryPoint = "/members/add";
variables.PUBLIC_API_KEY= "PUBLIC_API_KEY";
variables.PRIVATE_API_KEY= "PRIVATE_API_KEY";

// Call Emma
variables.myResponse = callEmma(variables.entryPoint,variables.PUBLIC_API_KEY,variables.PRIVATE_API_KEY,variables.dataFields);
variables.myResponse = deserializejson(variables.myResponse);

//Output to browser
if(variables.myResponse.added){
    writeoutput("Member " & variables.myResponse.member_id & " added!");
}
else{
    writeoutput("There was an error adding this member");
}
</cfscript>
Run Code Online (Sandbox Code Playgroud)

天哪!我一直在写太多的API ...我显然需要治疗!


Dan*_*rts 0

鉴于您提交数据的方式,您不必序列化字符串,只需

value='#serializejson(fields)#'
Run Code Online (Sandbox Code Playgroud)

从您的评论来看,这对您不起作用。不幸的是,在我看来,他们的文档对于如何发送数据感到困惑。他们说它应该是一个帖子,但随后只显示一个 json 对象。如果从 JS 使用,这可能很有用,但否则会令人困惑。

要缩小问题发生的范围,请尝试静态提交信息,例如获取示例代码并将其粘贴到字段的值中。您应该首先尝试在动态版本之前进行静态尝试。甚至可能是 CF json 序列化由于区分大小写或其他问题而出现问题。

<!--- add email --->
<cfhttpparam
  type="formfield"
  name="email"
  value='email@domain.com'
/>

<!--- add field: name_first --->
<cfhttpparam
  type="formfield"
  name="fields"
  value='{ "first_name": "myFirstName" }'
/>
<!--- or if that doesn't work also try value='"first_name": "myFirstName" ' --->
Run Code Online (Sandbox Code Playgroud)