¿Es cierto que Salesforce no permite realizar peticiones HTTP (callouts) con datos multipart/form-data desde APEX? Pues la respuesta es que Salesforce sí lo permite, lo único que tenemos que tener en cuenta es qué tipo de datos admite el servidor destino al que enviamos la petición.
Esto es muy importante cuando queremos subir archivos desde Salesforce (vía APEX, por supuesto) a un servidor externo a Salesforce, como Box o WatchDox, por ejemplo. Esto resulta económicamente rentable, cuando el coste de almacenamiento del servidor externo es inferior al coste de almacenaniento de Salesforce.
Lo primero que debemos averiguar es qué tipo de datos admite el servidor externo en las peticiones con datos multipart/form-data. Puede ser que el servidor externo sólo admita contenido binario, o puede ser que admita contenido codificado en base64, o ambos.
En el primero de los casos, en el que el servidor destino sólo admita contenido binario, Enrico Murro, miembro de Cloudspokes al igual que yo, ha desarrollado hace escasos días una magnífica solución, que puedes ver aquí. Se trata de una solución que muchos desarrolladores de Salesforce estaban esperando, y que gracias a este reto de Cloudspokes en el que ambos hemos participado, pudimos investigar a fondo sobre este tema.
Si el servidor destino admite también contenido codificado en base64, en este caso la solución es mucho más sencilla. Esta fue la opción que yo opté para este reto, ya que el servidor destino admitía ambos tipos de contenido.
A continuación muestro un código de ejemplo para este tipo de servidores:
public static HTTPResponse uploadFile(String fileName, Blob fileBody, String contentType){ // Body String boundary = String.valueOf(DateTime.now().getTime()); String body = '------------' + boundary + '\r\n'; body+='Content-Disposition: form-data; name="data"; filename="' + fileName + '"\r\n'; body+='Content-Transfer-Encoding: base64\r\n'; if ((contentType == null) || (contentType == '')){contentType = 'application/octet-stream';} body+='Content-Type: ' + contentType + '\r\n\r\n'; body+=EncodingUtil.base64Encode(fileBody); body+='\r\n------------' + boundary + '--'; // Instantiate a new HTTP request, specify the method (POST) as well as the endpoint HttpRequest req = new HttpRequest(); // Set headers req.setHeader('Content-Type', 'multipart/form-data; boundary=----------' + boundary); req.setHeader('Content-Length',String.valueof(body.length())); // Set body req.setBody(body); // Set method and endpoint req.setMethod('POST'); req.setEndpoint('http://www.TuServidorExterno.com'); // Send HTTP request and get HTTP response Http http = new Http(); return(http.send(req)); }
Puedes ver el vídeo de esta funcionalidad en acción correspondiente a mi solución creada para el reto de Cloudspokes, la cual permite subir todo tipo de archivos de hasta 3Mb a WatchDox desde Salesforce. Si el explorador web soporta HTML5, se pueden seleccionar varios archivos para subirlos en el mismo proceso, soporta drag&drop, y además se muestra una barra de progreso por cada archivo que se quiere subir.
Espero que este post os sirva de ayuda.
Hi, thanks for the nice post. i m having problem with calling REST API; i hope you can help out. i’m getting error: 400 (SalesforceProxy-Endpoint not defined in header.) … Thanks in advance.
here is my code:
// Get a reference to jQuery that we can work with
$j = jQuery.noConflict();
// Get an instance of the REST API client and set the session ID
var client = new forcetk.Client();
client.setSessionToken(‘{!$Api.Session_ID}’);
$j.ajax({
url: «https://csX.salesforce.com/services/proxy/apexrest/Contact/v27.0/doGet/»,
type: ‘GET’,
dataType: ‘json’,
error: function(data1) { alert(‘error’); alert(data1.errorCode); },
beforeSend: function (xhr) {
xhr.setRequestHeader(‘Authorization’, «OAuth » + client);
xhr.setRequestHeader(‘Accept’, «application/json»);
},
success: function() { alert(‘hello!’); }
});
Hi.
In your endpoint url, instead csX you must specify your organization id. For example, my sandbox is cs8.
Thanks so much for quick response. I did specify my org id. its cs9. i just changed it csX while posting here. Sorry for confusion… i should have mentioned earlier…
Hi again.
I don’t understand what you want to do?
I have created REST API:
@HttpGet
global static List getContact() {
List widgets = [SELECT Id, Name, Phone, sbscbr_num__c FROM Contact
];
return widgets;
}
I am trying to call this api through VF Page using JQuery ajax. I am able to call this API using workbench and getting JSON result also. But just not working through VF Page.
Hope i’m clear this time. Pls let me know if you have more questions. Thanks.
If you are invoking APEX REST method from Visualforce in same org, the best solution is: Use Javascript Remoting instead!
I agree but we are performing some test; moving forward we are planning to use REST API from different org also.
Then, try this url: https://cs9.salesforce.com/services/apexrest/Contact, if «Contact» is the urlMapping.
no luck. getting this error:
XMLHttpRequest cannot load https://cs9.salesforce.com/services/apexrest/Contact. Origin https://c.cs9.visual.force.com is not allowed by Access-Control-Allow-Origin.
Hi.
I send you an email.