web-dev-qa-db-ja.com

方法POST ColdfusionCFHTTPを使用したリモートAPIへのJSONデータ

私はこれを完全に失敗させていると確信していますが、Stack Overflowの仲間のユーザーの助けを借りてここまで到達したので、これまでのところ感謝しています。

POST JSONデータをリモート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 protected]"
}

これは私がこれまでに構築したものですが、リモート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>

繰り返しになりますが、私は確かにデータの構造をなんとかして操作していますが、特に "fields":{"first_name":を適切に設定することに関して、何が間違っているのかわかりません。 "myFirstName"}構造/配列。

13
goxmedia

リクエスト文字列を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>

2013年10月26日更新
最近APIを使用して行っているすべての作業について、見つけたこのケーシングを自動化する簡単な方法を更新すると思いました。 JSON Util ライブラリとBen Nadelの JSON Serializer Utility CFC の組み合わせを使用して、すべてのリターンのシリアル化の一貫性を大幅に向上させました。

以下は、私がこれをどのように実装したかの要点の例です。
https://Gist.github.com/timmaybrown/7226809

プロジェクトで永続エンティティCFCの使用に移行したとき、Ben NadelのシリアライザーCFCを、 getComponentMetaData() 関数を使用してすべての永続CFCのプロパティをループする独自の子CFCメソッドで拡張することがわかりました。個別のキーの構造と、それに続くシリアル化のためのケーシングを構築します。このアプローチにより、APIはエンティティ内のプロパティ名の大文字と小文字を自動的に継承でき、非常に便利です。 reinitには少しオーバーヘッドがありますが、APIでケーシングの一貫性を保つには十分な価値があります。

更新9/8/16Re:一貫したケーシングについての上記の私のポイント。私は新しいプロジェクトのためにデータベースで異なる列の命名規則に向かう傾向があるので、これらの問題の多くと戦う必要はありません。 firstNameなどの代わりにfirst_name

25
timbrown

更新:2012年9月26日:設定したデモアカウントでAPIキーをリクエストした後、may account_idと一緒にAPIキーを送信しました。以下にコードをドロップすると、メンバーを追加するための魅力のように機能しました。 。

それを言うことから始めましょう このコードはどれもテストされていません(上記の更新を参照)。 MyEmmaアカウントを持っていません。APIを使用するには、account_idの有料顧客である必要があるようです。それは吹く!しかし、これはあなたを本当に近づけるはずであり、ロジックをカプセル化するためのいくつかのアイデアをあなたに与えるかもしれません。それは私の執着になっています。

第二に、この投稿は9か月前のものであり、おそらく長い間理解しているか、宝くじに当選して現在その場所を運営していることを認識しています。したがって、誰もこの投稿を見ることさえできません。しかし、私は自分でいくつかの答えを探していて、それに遭遇しました...そしてJSONの作成と解析は私の日常生活の一部であるため、これは私が常に自分自身をまっすぐに設定し続ける必要があるものです。それで、あなたの質問への迅速な答えであることが判明したものは、深夜、自己奉仕的、強迫的な挑戦になりました。いずれにせよ...

... JSONで行っているのは、クライアント側のネストされた構造を作成することです。 2つのキーと値のペア(フィールドと電子メール)を持つルート構造があります。次に、構造体 'fields'は、その電子メールアドレス(first_name)に対して送信するキーと値のペアを持つ構造体を保持します。おそらくあなたはもっと送ることができます。

ネストされた構造を構築しています。構造体のキーは構造体を保持できることに注意してください。そして、それらのキーは構造などを保持できます。それはあなたが行きたいのと同じくらい暗くて厄介になることがあります。しかし、それがすべてJSONです...それはクライアント側のオブジェクトです。

これがデータビルドとJSONオブジェクトです...

<cfscript>
    variables.dataFields = {};
    variables.dataFields['fields'] = {};
    variables.dataFields['email'] = "[email protected]";
    variables.dataFields.fields['first_name'] = "myFirstName";
    variables.dataFields = serializejson(variables.dataFields);
</cfscript>

配列表記を使用して構造キー名を明示的に設定していることに注意してください。Coldfusionで大文字と小文字を制御するには、これを行う必要があります。そうしないと、キーはすべて大文字になります。 ..大文字と小文字を区別するJavaScriptは必要ありません。これは、発生している問題の一部である可能性があります。

エマが事件のために理解しないならば、あなたはあなたを得るでしょう...

{"error": "Unable to parse JSON request"}

しかし、配列表記を使用してキー名を明示的に設定し、オブジェクトをシリアル化すると、素敵できれいな、古き良きファッションのJSONが得られます...

{"fields":{"first_name":"myFirstName"},"email":"[email protected]"}

以下では、Emmaへのhttpリクエストを関数に入れました。 Content-Typeヘッダーをapplication/jsonとして設定することも非常に重要です。これにより、ブラウザーはそれをテキスト文字列だけでなくオブジェクト。そしてリクエストの本文としてJSONを送信しています、 'fields'というフォームフィールドではありません...あなたがそれを大声で言うとき、うまくいけばそれは理にかなっています。これが機能です...

<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>

次に、もう一度、JSONビルド(ネストされた構造)を示します...

<cfscript>
    variables.dataFields = {};
    variables.dataFields['fields'] = {};
    variables.dataFields['email'] = "[email protected]";
    variables.dataFields.fields['first_name'] = "myFirstName";
    variables.dataFields = serializejson(variables.dataFields);
</cfscript>

次に、関数に渡す変数を設定します...

<cfscript>
    variables.entryPoint = "/members/add";
    variables.PUBLIC_API_KEY= "PUBLIC_API_KEY";
    variables.PRIVATE_API_KEY= "PRIVATE_API_KEY";
</cfscript>

次に電話をかけます...

<cfscript>
    variables.myResponse = callEmma(variables.entryPoint,variables.PUBLIC_API_KEY,variables.PRIVATE_API_KEY,variables.dataFields);
    variables.myResponse = deserializejson(variables.myResponse);
</cfscript>

次に、応答を取得して逆シリアル化し、必要に応じて変数を出力します。

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

もう、私は通常、できる限り<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>

私の神様!私はあまりにも多くのAPIを書いてきました...私は明らかに治療が必要です!

8
Steve Reich

あなたが言及した構造

{"fields":{"first_name": "myFirstName"}、 "email": "[email protected]"}このJSON for'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>
1
krishna Ram

データを送信する方法を考えると、文字列をシリアル化する必要はありません。

value='#serializejson(fields)#'

あなたのコメントから、それはあなたにとってうまくいきませんでした。残念ながら、彼らのドキュメントは、データの送信方法に関してIMOを混乱させています。彼らはそれが投稿であるべきだと言いますが、それからjsonオブジェクトだけを示します。 JSから使用する場合は便利かもしれませんが、それ以外の場合は混乱します。

問題が発生している場所を絞り込むには、情報を静的に送信してみてください。たとえば、サンプルコードを取得して、フィールドの値に貼り付けます。最初に、動的バージョンの前に静的な試行を行うようにしてください。 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" ' --->
0
Dan Roberts

偶然のタイミング。その点で、私たちは現在同じ問題に取り組んでいます。

現在、CFバージョンを8から9.01に更新する作業を行っており、cfajaxproxyを使用するコードがいくつかあります(9.01では実行できません)が、CF8では正常に機能します。

(1)問題の実際の根本原因が何であるかについては未定です。時間があれば、より具体的にするためにさらに作業を行います...しかし、回避策は、ajaxを介して呼び出されているコードをWebルートに配置することです。

(1)仮想ディレクトリの使用が原因であるか、CFアプリケーションフレームワーク(CFIDEスクリプトがファイルに自動的に挿入される)の影響を受けて、返されるJSONの予想される形式が混乱している可能性があります。

アドビでバグを記録しました。

0
Gavin Baumanis