2012-01-19 29 views
11

Sono sicuro di essere completamente in errore, ma ho ottenuto questo risultato con l'aiuto di altri utenti di Stack Overflow, quindi grazie finora.Come inviare i dati JSON all'API remota utilizzando Coldfusion CFHTTP

Ho bisogno di inviare i dati JSON a un'API remota. Ovviamente non posso usare jQuery a causa di problemi SOP e l'API remota non supporta JSONP.

Inoltre, non voglio dover utilizzare alcun tipo di proxy per aggirare le limitazioni SOP.

Per la documentazione API (http://myemma.com/api-docs/), questo è la formattazione dei dati che si aspettano (richiesta e risposta di dati viene trasmesso come JSON):

POST https://api.e2ma.net//123/members/add 
{ 
    "fields": { 
    "first_name": "myFirstName" 
    }, 
    "email": "[email protected]" 
} 

e questo è quello che ho costruito finora, ma continuo a ricevere "in grado di analizzare JSON" errori dal API remota:

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

Ancora una volta, sto sicuramente pressare la struttura dei miei dati in qualche modo, ma Non sono sicuro di cosa sto facendo male, in particolare per quanto riguarda l'impostazione corretta dei "campi" : {"first_name": "myFirstName"} structure/array.

risposta

0

Dato il modo in cui si sta presentando i dati non si dovrebbe avere per serializzare le corde, appena

value='#serializejson(fields)#' 

Dal tuo commento, che non ha funzionato per voi. Sfortunatamente i loro documenti stanno confondendo IMO su come i dati dovrebbero essere inviati. Dicono che dovrebbe essere un post ma poi mostrare solo un oggetto JSON. Forse è utile se si utilizza da JS ma confondendo altrimenti.

Per restringere il punto in cui si è verificato il problema, provare ad inviare staticamente le informazioni, ad esempio prendere il loro codice di esempio e incollare i valori dei campi. Dovresti prima provare a ottenere un tentativo statico prima di una versione dinamica. Può anche darsi che la serializzazione di CF json stia inciampando a causa di case-sensitive o di altri problemi.

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

capisco quello che stai dicendo, ma sto ancora ricevendo il { "errore": "Impossibile analizzare JSON richiesta"} indietro dall'API. Ciò significa che non invierò la "email": "email @ dominio.com "o" campi ": { " first_name ":" myFirstName "} parametri correttamente nel mio esempio sopra.Questo è quello che ho bisogno di aiuto con. Passando quelle stringhe JSON correttamente –

0

Tempi fortuiti. In questo stiamo attualmente lavorando allo stesso problema.

Attualmente stiamo lavorando all'aggiornamento della nostra versione CF da 8 a 9.01 e disponiamo di un codice che utilizza cfajaxproxy - che non riesce a girare sotto 9.01 - ma funziona bene in CF8.

Sono indeciso (1) su quale sia l'effettiva causa principale del problema; Se avrò un po 'di tempo, farò qualche altro lavoro per essere più specifico ... ma la soluzione è mettere il codice che viene chiamato tramite ajax nel webroot.

(1) potrebbe essere causato dall'uso di directory virtuali, o forse è influenzato dal framework CF Application - in cui gli script CFIDE vengono automaticamente inseriti nei file - e crea problemi con il formato previsto del JSON restituito.

Ho riscontrato un errore con Adobe.

+0

Stai descrivendo un altro problema ma apprezzo l'input . –

18

È necessario inviare la stringa di richiesta come tipo di corpo httpparam. Il corpo della richiesta potrebbe essere qualcosa come l'intero ambito del modulo della struttura preparata.Assicuratevi di usare la notazione array per impostare le vostre chiavi di struttura o metterle in "virgolette" durante la creazione della struttura implicita per assicurarvi che mantengano il loro involucro corretto quando avviene serializeJSON() altrimenti ColdFusion scalderà le chiavi della struttura.

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

Aggiornamento 10/26/13
per tutto il lavoro che ho fatto ultimamente con le API ho pensato di aggiornare un modo semplice per automatizzare questo involucro che ho trovato. Ho utilizzato una combinazione della libreria JSON Util e quella di Ben Nadel JSON Serializer Utility CFC per ottenere una coerenza di serializzazione molto migliore per tutti i resi.

Di seguito è riportato un esempio GIST di come ho implementato questo.
https://gist.github.com/timmaybrown/7226809

Come ho passato alla utilizzare entità persistente CFC nei miei progetti, ho scoperto che l'estensione CFC serializzatore di Ben Nadel con il mio metodo bambino CFC che loop tutte le proprietà di mio CFC persistente utilizzando la funzione getComponentMetaData() di costruire una struttura di chiavi distinte e l'involucro per la serializzazione da seguire. L'approccio consente alla mia API di ereditare automaticamente l'involucro dei nomi delle mie proprietà all'interno delle mie entità ed è molto utile. Un po 'di overhead su reinit, ma ne vale la pena per mantenere l'involucro coerente nella tua API.

Aggiornamento 9/8/16 Re: il mio punto di sopra dell'involucro coerente. Ho cercato una diversa convenzione di denominazione delle colonne nei miei database per i progetti più recenti, quindi non devo lottare con molti di questi problemi. first_name invece di firstName ecc.

+0

Se ColdFusion presenta gli eventuali problemi durante la serializzazione si può anche solo costruire il corpo in una stringa come ' Questo si tradurrebbe in questa stringa JSON '{" fields ": {" first_name ":" myFirstName "," is_active ": true}," email ":" [email protected] "}' – timbrown

+0

@ user1113083 - se questo fosse utile dovresti contrassegnarlo come la risposta corretta per gli altri . – timbrown

8

AGGIORNAMENTO: 26/09/2012: Dopo aver richiesto una chiave API con l'account demo che ho impostato, me ne hanno inviato uno insieme a may account_id. Ho lasciato cadere il codice in basso e ha funzionato come un fascino per l'aggiunta di un membro.

Vorrei iniziare dicendo che nessuno di questo codice è testato (vedere Update sopra). Non ho un account MyEmma e apparentemente devi essere un cliente pagante per un account_id per utilizzare l'API. Che soffia! Ma questo dovrebbe portarti molto vicino e potrebbe darti delle idee per incapsulare la logica, che è diventata la mia ossessione.

In secondo luogo, mi rendo conto che questo post ha 9 mesi e probabilmente lo avete capito da tempo, o avete vinto la lotteria e ora gestite il posto. Quindi nessuno potrà mai nemmeno vedere questo post. Ma stavo cercando alcune risposte io stesso e l'ho incontrato ... e dal momento che la formulazione e l'analisi di JSON sono parte della mia vita quotidiana, questo è qualcosa che ho sempre bisogno di continuare a mettere su me stesso. Quindi quella che si è rivelata una rapida risposta alla tua domanda, è diventata una sfida a tarda notte, egoista, ossessiva. In ogni caso ...

... quello che stai facendo con JSON, sta creando strutture nidificate lato client. Hai la struttura radice con due coppie di valori-chiave (campi e email). Quindi i "campi" della struttura contengono una struttura con la coppia di valori-chiave che invii per quell'indirizzo email (first_name). Presumibilmente è possibile inviare di più.

Si stanno costruendo strutture nidificate. Ricorda che una chiave in una struttura può contenere una struttura. E quelle chiavi possono contenere strutture e così via. Può diventare oscuro e cattivo come vuoi andare. Ma questo è tutto JSON è ... è un oggetto lato client.

Quindi ecco la tua build di dati e l'oggetto JSON ...

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

Nota che sto impostare esplicitamente i nomi dei tasti della struttura con la notazione di matrice. Dobbiamo farlo per controllare il caso con Coldfusion. Altrimenti, le chiavi saranno tutte maiuscole ... non vogliamo che vogliamo per JavaScript sensibile al maiuscolo/minuscolo. Questo potrebbe essere parte del problema che stai avendo.

Se Emma non capisce a causa del caso, allora si dovrebbe ottenere il vostro ...

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

Ma quando abbiamo fissato i nostri nomi in modo esplicito chiave usando la notazione array, quindi serializzare il nostro oggetto, abbiamo diventa bello e carino, buon vecchio stile JSON ...

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

Quindi di seguito, ho inserito la nostra richiesta http su Emma in una funzione. È anche molto importante per impostare l'intestazione Content-Type come application/json, quindi il browser lo invierà come un oggetto e non solo una stringa di testo. E stiamo inviando il nostro JSON come il corpo della nostra richiesta, non in un campo modulo chiamato 'campi' ... speriamo che abbia senso quando lo dici ad alta voce. Ecco la funzione ...

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

Poi, ancora una volta, ecco il nostro accumulo JSON (strutture annidate) ...

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

Poi abbiamo impostato le variabili da passare alla funzione ...

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

Poi fare la telefonata ...

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

Quindi prendiamo la nostra risposta, deserializziamo e produciamo le variabili come vogliamo.

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

più, io di solito uso <cfscript> per quanto mi è possibile. È più facile da leggere e mi fa sentire molto più intelligente di quanto lo sia io. Così, quando abbiamo messo tutto insieme, per il taglio-e-incolla, abbiamo questo ...

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

mio Dio! Ho scritto WAY troppe API ... Ho chiaramente bisogno di terapia!

1

La struttura che hai citato

{ "campi": { "first_name": "myFirstName" }, "e-mail": "[email protected]" } In questo JSON per il valore-chiave 'campi' è di nuovo un JSON Quindi, si può andare come 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> 
Problemi correlati