如何使用 Coldfusion CFHTTP 将 JSON 数据发布到远程 API
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8932973/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to POST JSON Data to Remote API Using Coldfusion CFHTTP
提问by goxmedia
I'm sure that I'm completely botching this up but I got this far with the help of fellow Stack Overflow users, so thanks thus far.
我确信我完全搞砸了,但我在 Stack Overflow 用户的帮助下走到了这一步,所以到目前为止非常感谢。
I need to POST JSON data to a remote API. Obviously I can't use jQuery due to SOP issues, and the remote API does not support JSONP.
我需要将 JSON 数据发布到远程 API。显然由于 SOP 问题我无法使用 jQuery,并且远程 API 不支持 JSONP。
I also don't want to have to use any type of proxy as to get around the SOP limitations.
我也不想使用任何类型的代理来绕过 SOP 限制。
Per the API docs (http://myemma.com/api-docs/), this is the formatting of the data they expect (request and response data is transferred as JSON):
根据 API 文档 ( http://myemma.com/api-docs/),这是他们期望的数据格式(请求和响应数据以 JSON 格式传输):
POST https://api.e2ma.net//123/members/add
{
"fields": {
"first_name": "myFirstName"
},
"email": "[email protected]"
}
And this is what I've built thus far but continue to receive "unable to parse JSON" errors from the remote API:
这是我迄今为止构建的内容,但继续从远程 API 接收“无法解析 JSON”错误:
<cfset fields[name_first]="#SerializeJSON( "myFirstName" )#" />
<cfset form.email="#SerializeJSON( "[email protected]" )#" />
<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>
Again, I'm surely mangling the structure of my data somehow, but I'm not sure what I'm doing wrong, particularly regarding properly setting the "fields": { "first_name": "myFirstName" }structure/array.
同样,我肯定会以某种方式修改我的数据结构,但我不确定我做错了什么,特别是关于正确设置“字段”:{“first_name”:“myFirstName”}结构/数组。
回答by timbrown
You should send your request string as the httpparam type of body. The body of the request could be something like the entire form scope of your prepped structure. Be sure to either use array notation for setting your structure keys or put them in "quotes" during the implicit structure creation to ensure they retain their proper casing when the serializeJSON() takes place otherwise ColdFusion will uppercase the structure keys.
您应该将请求字符串作为正文的 httpparam 类型发送。请求的主体可能类似于您准备好的结构的整个表单范围。确保使用数组符号来设置结构键或在隐式结构创建期间将它们放在“引号”中,以确保它们在 serializeJSON() 发生时保留其正确的大小写,否则 ColdFusion 会将结构键大写。
<cfset stFields = {
"fields" = {
"first_name" = "myFirstName"
},
"email" = "[email protected]"
}>
<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>
Update 10/26/13
For all the work I've been doing lately with APIs I thought I'd update an easy way to automate this casing that I've found. I've used a combination of the JSON Utillibrary and Ben Nadel's JSON Serializer Utility CFC to accomplish much better serialization consistency for all returns.
2013 年 10 月 26 日更新
对于我最近使用 API 所做的所有工作,我想我会更新一种简单的方法来自动化我发现的这种情况。我使用了JSON Util库和 Ben Nadel 的JSON Serializer Utility CFC 的组合来为所有返回实现更好的序列化一致性。
Below is an example GIST of how I've implemented this.
https://gist.github.com/timmaybrown/7226809
下面是我如何实现这一点的 GIST 示例。
https://gist.github.com/timmaybrown/7226809
As I've transitioned to using persistent entity CFCs in my projects, I've found that extending Ben Nadel's serializer CFC with my own child CFC method that loops all my persistent cfc's properties using the getComponentMetaData()function to build a structure of distinct keys and the casing for the serialization to follow. The approach allows my api to inherit automatically the casing of my property names within my entities and is very useful. A bit of overhead on reinit, but well worth it to keep your casing consistent in your API.
当我转而在我的项目中使用持久性实体 CFC 时,我发现使用我自己的子 CFC 方法扩展 Ben Nadel 的序列化器 CFC,该方法使用getComponentMetaData()函数循环我所有持久性 cfc 的属性以构建不同键的结构以及后续序列化的外壳。该方法允许我的 api 在我的实体中自动继承我的属性名称的大小写,并且非常有用。在 reinit 上有一点开销,但在 API 中保持大小写一致是非常值得的。
Update 9/8/16Re: my point above about consistent casing. I have trended toward a different column naming convention in my databases for newer projects so I don't have to fight with a lot of these issues. first_nameinstead of firstNameetc.
2016 年 9 月 8 日更新回复:我上面关于一致外壳的观点。对于较新的项目,我倾向于在我的数据库中采用不同的列命名约定,因此我不必与很多这些问题作斗争。first_name而不是firstName等。
回答by Steve Reich
UPDATE: 9/26/2012: After requesting an API Key with the demo account I set up, they sent me one along with may account_id. I dropped the code in below and it worked like a charm for adding a member.
更新:2012 年 9 月 26 日:在使用我设置的演示帐户请求 API 密钥后,他们向我发送了一个和 May account_id。我将代码放在下面,它就像添加成员的魅力一样。
Let me start by saying that none of this code is tested(see update above). I don't have a MyEmma account, and apparently you have to be a paying customer for an account_id to use the API. That blows! But this should get you real close and may give you some ideas for encapsulating logic, which has become my obsession.
首先让我说这些代码都没有经过测试(请参阅上面的更新)。我没有 MyEmma 帐户,显然您必须成为 account_id 的付费客户才能使用该 API。那吹!但这应该会让你真正接近,并且可能会给你一些封装逻辑的想法,这已经成为我的痴迷。
Secondly, I realize this post is 9 months old and you have probably either long figured it out, or won the lottery and are running the place by now. So no one may ever even see this post. But I was looking for some answers myself and ran across it... and since formulating and parsing JSON is part of my daily life, this is something I always need to keep setting myself straight on. So what turned out to be a quick answer to your question, became a late night, self serving, obsessive challenge. At any rate...
其次,我意识到这篇文章已经有 9 个月的历史了,你可能早就想通了,或者中了彩票并且现在正在经营这个地方。所以没有人可能会看到这篇文章。但是我自己一直在寻找一些答案并遇到了它......而且由于制定和解析 JSON 是我日常生活的一部分,这是我总是需要让自己直截了当的事情。因此,结果是对您的问题的快速回答,变成了深夜、自我服务、强迫性的挑战。好歹...
...what you are doing with JSON, is creating client side nested structures. You have the root structure with two key-value pairs (fields and email). Then the structure 'fields' holds a structure with the a key-value pair you are sending over for that email address (first_name). Presumably you can send more.
...您正在使用 JSON 做的是创建客户端嵌套结构。您拥有包含两个键值对(字段和电子邮件)的根结构。然后结构“字段”包含一个结构,其中包含您为该电子邮件地址 (first_name) 发送的键值对。想必你可以发送更多。
You are building nested structures. Remember that a key in a structure can hold a structure. And those keys can hold structures, and so on. It can get as dark and nasty as you want to go. But that's all JSON is... it's a client side object.
您正在构建嵌套结构。请记住,结构中的键可以保存结构。这些键可以保存结构,等等。它可以变得像你想要的那样黑暗和肮脏。但这就是 JSON 的全部内容……它是一个客户端对象。
So here is your data build and JSON object...
所以这是您的数据构建和 JSON 对象...
<cfscript>
variables.dataFields = {};
variables.dataFields['fields'] = {};
variables.dataFields['email'] = "[email protected]";
variables.dataFields.fields['first_name'] = "myFirstName";
variables.dataFields = serializejson(variables.dataFields);
</cfscript>
Note that I'm explicitly setting the structure key names with array notation. We have to do this to control the case with Coldfusion. Otherwise, the keys will be in all caps... not want we want for case sensitive JavaScript. This could be part of the problem you are having.
请注意,我使用数组表示法明确设置了结构键名称。我们必须这样做以控制 Coldfusion 的情况。否则,键将全部大写......不希望我们想要区分大小写的 JavaScript。这可能是您遇到的问题的一部分。
If Emma doesn't understand because of case, then you would get your...
如果艾玛因为大小写不明白,那么你会得到你的......
{"error": "Unable to parse JSON request"}
But when we explicitly set our key names using array notation, and then serialize our object, we get nice and pretty, good ol' fashion JSON...
但是当我们使用数组符号显式设置我们的键名,然后序列化我们的对象时,我们会得到漂亮漂亮的时尚 JSON ......
{"fields":{"first_name":"myFirstName"},"email":"[email protected]"}
So below, I put our http request to Emma in a function. It is also very important to set the Content-Type header as application/json, so the browser will send it as a object and not just a text string. And we are sending our JSON as the body of our request,not in a form field called 'fields'... hopefully that makes sense when you say it out loud. Here's the function...
所以在下面,我将我们对 Emma 的 http 请求放在一个函数中。将 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>
Then once again, here is our JSON build (nested structures)...
然后再一次,这是我们的 JSON 构建(嵌套结构)...
<cfscript>
variables.dataFields = {};
variables.dataFields['fields'] = {};
variables.dataFields['email'] = "[email protected]";
variables.dataFields.fields['first_name'] = "myFirstName";
variables.dataFields = serializejson(variables.dataFields);
</cfscript>
Then we set the variables to pass to the function...
然后我们设置要传递给函数的变量...
<cfscript>
variables.entryPoint = "/members/add";
variables.PUBLIC_API_KEY= "PUBLIC_API_KEY";
variables.PRIVATE_API_KEY= "PRIVATE_API_KEY";
</cfscript>
Then make the phone call...
然后打电话...
<cfscript>
variables.myResponse = callEmma(variables.entryPoint,variables.PUBLIC_API_KEY,variables.PRIVATE_API_KEY,variables.dataFields);
variables.myResponse = deserializejson(variables.myResponse);
</cfscript>
We then take our response, deserialize it, and output the variables however we want.
然后我们获取我们的响应,反序列化它,然后输出我们想要的变量。
<cfscript>
if(variables.myResponse.added){
writeoutput("Member " & variables.myResponse.member_id & " added!");
}
else{
writeoutput("There was an error adding this member");
}
</cfscript>
Anymore, I generally use <cfscript>as much as I can. It's easier to read and it makes me feel much smarter than I really am. So when we put it all together, for cut-and-paste, we have this...
无论如何,我通常会<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 protected]";
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>
My GOD! I've been writing WAY too many API's... I clearly need therapy!
天哪!我写了太多 API 了……我显然需要治疗!
回答by krishna Ram
The structure you have mentioned
你提到的结构
{ "fields": { "first_name": "myFirstName" }, "email": "[email protected]" } In this JSON for 'fields' key value is again a JSON So, you can go like dis
{ "fields": { "first_name": "myFirstName" }, "email": "[email protected]" } 在这个 JSON 中,'fields' 键值又是一个 JSON 所以,你可以像 dis
<cfscript>
VARIABLES.postJSON = StructNew();
VARIABLES.nameJSON = StructNew();
StructInsert(VARIABLES.nameJSON, 'first_name','myFirstName');
StructInsert(VARIABLES.postJSON, 'fields',VARIABLES.nameJSON);
StructInsert(VARIABLES.postJSON, 'email','[email protected]');
</cfscript>
<cfhttp
url="https://api.e2ma.net/123/members/add"
method="POST"
username="username"
password="pssword"
useragent="#CGI.http_user_agent#"
result="objGet">
<cfhttpparam
type="body"
name="field"
value='#SerializeJSON(VARIABLES.postJSON)#'
/>
</cfhttp>
<cfoutput>#objGet.FileContent#</cfoutput>
回答by Dan Roberts
Given the way you are submitting the data you should not have to serialize the strings, just
鉴于您提交数据的方式,您不必序列化字符串,只需
value='#serializejson(fields)#'
From your comment, that didn't work for you. Unfortunately their docs are confusing IMO as to how the data should be sent. They say it should be a post but then show only a json object. Maybe that is useful if using from JS but confusing otherwise.
从您的评论来看,这对您不起作用。不幸的是,他们的文档让 IMO 对如何发送数据感到困惑。他们说它应该是一个帖子,但只显示一个 json 对象。如果从 JS 使用,这可能很有用,但否则会令人困惑。
To narrow down where the problem is occurring try submitting the information statically, for example take their example code and paste into the values of the fields. You should first try to get a static attempt going before a dynamic version. It may even be that the CF json serialization is tripping things up due to case-sensitivity or other issues.
要缩小问题发生的范围,请尝试静态提交信息,例如获取他们的示例代码并粘贴到字段的值中。您应该首先尝试在动态版本之前进行静态尝试。甚至可能是 CF json 序列化由于区分大小写或其他问题而导致问题。
<!--- add email --->
<cfhttpparam
type="formfield"
name="email"
value='[email protected]'
/>
<!--- 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" ' --->
回答by Gavin Baumanis
Fortuitous timing. In that we are currently working through the same issue.
机缘巧合。我们目前正在解决同样的问题。
We are currently working on updating our CF version from 8 to 9.01 and have some code that uses cfajaxproxy - that fails to run under 9.01 - but works fine in CF8.
我们目前正在将 CF 版本从 8 更新到 9.01,并且有一些使用 cfajaxproxy 的代码 - 在 9.01 下无法运行 - 但在 CF8 中运行良好。
I am undecided (1) as to what the actual root cause of the issue is; If I get some time, I will do some more work to be more specific... but the workaround is to put the code that is being called via ajax in the webroot.
我不确定 (1) 问题的实际根本原因是什么;如果我有时间,我会做一些更具体的工作……但解决方法是将通过 ajax 调用的代码放在 webroot 中。
(1) it might be caused by the use of virtual directories, or perhaps is effected by the CF Application framework - whereby CFIDE scripts are automatically inserted into files - and messes with the expected format of the JSON returned.
(1) 它可能是由使用虚拟目录引起的,或者可能是受 CF 应用程序框架的影响 - CFIDE 脚本由此自动插入到文件中 - 并且与返回的 JSON 的预期格式混淆。
I have logged a bug with Adobe.
我已经记录了 Adobe 的错误。

