'Getting invalid credentials on REST API
I'm trying to send a HttpPOST with Content-Type multipart/form-data using Apex, the API response is 'invalid credentials', the problem is not in the data because it works on Postman, I can't find where is the problem in my code:
My class:
public with sharing class XXXXXXXXXXXX {
@future(callout = true)
public static void fileUpload(){
String endpoint = 'endpoint';
List<ContentVersion> cvList = new List<ContentVersion>();
cvList = [SELECT id, Title, ContentDocumentId, FileExtension, VersionData FROM ContentVersion WHERE Id = '0685e000004zxRrAAI' LIMIT 1];
System.debug('Callout Log 1: ' + cvList);
if(!cvList.isEmpty()){
//form fileName with cv ID
String fileName = cvList[0].Id;
if(cvList[0].FileExtension != null && cvList[0].FileExtension != ''){
fileName = fileName + '.' + cvList[0].FileExtension;
}
System.debug('Callout log2: ' + fileName);
//callout ePOR service
String contentType = EinsteinVision_HttpBodyPart.GetContentType();
//Compose the form
string form64 = '';
form64 += EinsteinVision_HttpBodyPart.WriteBoundary();
form64 += EinsteinVision_HttpBodyPart.WriteBodyParameter('message', 'message value');
form64 += EinsteinVision_HttpBodyPart.WriteBoundary();
form64 += EinsteinVision_HttpBodyPart.WriteBodyParameter('login', 'login value');
form64 += EinsteinVision_HttpBodyPart.WriteBoundary();
form64 += EinsteinVision_HttpBodyPart.WriteBodyParameter('password', 'password value');
form64 += EinsteinVision_HttpBodyPart.WriteBoundary();
form64 += EinsteinVision_HttpBodyPart.WriteBodyParameter('to', 'to value');
form64 += EinsteinVision_HttpBodyPart.WriteBoundary();
form64 += EinsteinVision_HttpBodyPart.WriteBlobBodyParameter('image', EncodingUtil.base64Encode(cvList[0].VersionData), fileName);
Blob formBlob = EncodingUtil.base64Decode(form64);
String contentLength = String.valueOf(formBlob.size());
System.debug('Callout Log 3: ' + formBlob.size());
if(formBlob.size() > 12000000){
System.debug('Callout error');
}else{
Http http = new Http();
HttpRequest req = new HttpRequest();
req.setEndpoint(endpoint);
req.setMethod('POST');
req.setHeader('Content-Length', contentLength);
req.setHeader('Content-Type', contentType);
req.setTimeout(120000);
HttpResponse response = http.send(req);
String responseBody = response.getBody();
System.debug('Callout Log 4: ' + response.getStatusCode());
System.debug('Callout Log 5: ' + String.valueOf(response.getBody()));
}
}
}
}
Class to handle Content-Type multipart/form-data:
public virtual class EinsteinVision_HttpBodyPart {
public EinsteinVision_HttpBodyPart() {
}
// The boundary is alligned so it doesn't produce padding characters when base64 encoded.
private final static string Boundary = '1ff13444ed8140c7a32fc4e6451aa76d';
public static String getBoundary() {
return Boundary;
}
/**
* Returns the request's content type for multipart/form-data requests.
*/
public static string GetContentType() {
return 'multipart/form-data; charset="UTF-8"; boundary="' + Boundary + '"';
}
/**
* Pad the value with spaces until the base64 encoding is no longer padded.
*/
public static string SafelyPad(
string value,
string valueCrLf64,
string lineBreaks) {
string valueCrLf = '';
blob valueCrLfBlob = null;
while (valueCrLf64.endsWith('=')) {
value += ' ';
valueCrLf = value + lineBreaks;
valueCrLfBlob = blob.valueOf(valueCrLf);
valueCrLf64 = EncodingUtil.base64Encode(valueCrLfBlob);
}
return valueCrLf64;
}
/**
* Write a boundary between parameters to the form's body.
*/
public static string WriteBoundary() {
string value = '--' + Boundary + '\r\n';
blob valueBlob = blob.valueOf(value);
return EncodingUtil.base64Encode(valueBlob);
}
/**
* Write a boundary at the end of the form's body.
*/
public static string WriteBoundary(
EndingType ending) {
string value = '';
if (ending == EndingType.Cr) {
// The file's base64 was padded with a single '=',
// so it was replaced with '\r'. Now we have to
// prepend the boundary with '\n' to complete
// the line break.
value += '\n';
} else if (ending == EndingType.None) {
// The file's base64 was not padded at all,
// so we have to prepend the boundary with
// '\r\n' to create the line break.
value += '\r\n';
}
// Else:
// The file's base64 was padded with a double '=',
// so they were replaced with '\r\n'. We don't have to
// do anything to the boundary because there's a complete
// line break before it.
value += '--' + Boundary + '--';
blob valueBlob = blob.valueOf(value);
return EncodingUtil.base64Encode(valueBlob);
}
/**
* Write a key-value pair to the form's body.
*/
public static string WriteBodyParameter(
string key,
string value) {
string contentDisposition = 'Content-Disposition: form-data; name="' + key + '"';
string contentDispositionCrLf = contentDisposition + '\r\n\r\n';
blob contentDispositionCrLfBlob = blob.valueOf(contentDispositionCrLf);
string contentDispositionCrLf64 = EncodingUtil.base64Encode(contentDispositionCrLfBlob);
string content = SafelyPad(contentDisposition, contentDispositionCrLf64, '\r\n\r\n');
string valueCrLf = value + '\r\n';
blob valueCrLfBlob = blob.valueOf(valueCrLf);
string valueCrLf64 = EncodingUtil.base64Encode(valueCrLfBlob);
content += SafelyPad(value, valueCrLf64, '\r\n');
return content;
}
/**
* Write a key-value pair to the form's body for a blob.
*/
public static string WriteBlobBodyParameter(string key, string file64, string fileName) {
String mimeType = resolveMimeType(fileName);
string contentDisposition = 'Content-Disposition: form-data; name="' + key + '"; filename="'+fileName+'"';
string contentDispositionCrLf = contentDisposition + '\r\n';
blob contentDispositionCrLfBlob = blob.valueOf(contentDispositionCrLf);
string contentDispositionCrLf64 = EncodingUtil.base64Encode(contentDispositionCrLfBlob);
string content = SafelyPad(contentDisposition, contentDispositionCrLf64, '\r\n');
string contentTypeHeader = 'Content-Type: ' + mimeType;
string contentTypeCrLf = contentTypeHeader + '\r\n\r\n';
blob contentTypeCrLfBlob = blob.valueOf(contentTypeCrLf);
string contentTypeCrLf64 = EncodingUtil.base64Encode(contentTypeCrLfBlob);
content += SafelyPad(contentTypeHeader, contentTypeCrLf64, '\r\n\r\n');
integer file64Length = file64.length();
String last4Bytes = file64.substring(file64.length()-4,file64.length());
// Avoid padding the file data with spaces, which SafelyPad does
// http://salesforce.stackexchange.com/a/33326/102
EndingType ending = EndingType.None;
if (last4Bytes.endsWith('==')) {
// The '==' sequence indicates that the last group contained only one 8 bit byte
// 8 digit binary representation of CR is 00001101
// 8 digit binary representation of LF is 00001010
// Stitch them together and then from the right split them into 6 bit chunks
// 0000110100001010 becomes 0000 110100 001010
// Note the first 4 bits 0000 are identical to the padding used to encode the
// second original 6 bit chunk, this is handy it means we can hard code the response in
// The decimal values of 110100 001010 are 52 10
// The base64 mapping values of 52 10 are 0 K
// See http://en.wikipedia.org/wiki/Base64 for base64 mapping table
// Therefore, we replace == with 0K
// Note: if using \n\n instead of \r\n replace == with 'oK'
last4Bytes = last4Bytes.substring(0,2) + '0K';
file64 = file64.substring(0,file64.length()-4) + last4Bytes;
// We have appended the \r\n to the Blob, so leave footer as it is.
ending = EndingType.CrLf;
} else if (last4Bytes.endsWith('=')) {
// '=' indicates that encoded data already contained two out of 3x 8 bit bytes
// We replace final 8 bit byte with a CR e.g. \r
// 8 digit binary representation of CR is 00001101
// Ignore the first 2 bits of 00 001101 they have already been used up as padding
// for the existing data.
// The Decimal value of 001101 is 13
// The base64 value of 13 is N
// Therefore, we replace = with N
last4Bytes = last4Bytes.substring(0,3) + 'N';
file64 = file64.substring(0,file64.length()-4) + last4Bytes;
// We have appended the CR e.g. \r, still need to prepend the line feed to the footer
ending = EndingType.Cr;
}
content += file64;
content += WriteBoundary(ending);
return content;
}
private static String resolveMimeType(String fileName) {
String fileType = fileName.substringAfterLast('.');
String mimeType = 'image/png'; // fallback value
if (fileType.equalsIgnoreCase('png')) {
mimeType = 'image/png';
} else if (fileType.equalsIgnoreCase('jpeg') || fileType.equalsIgnoreCase('jpg')) {
mimeType = 'image/jpg';
} else if (fileType.equalsIgnoreCase('pgm')) {
mimeType = 'image/x-portable-graymap';
} else if (fileType.equalsIgnoreCase('ppm')) {
mimeType = 'image/x-portable-pixmap';
}
return mimeType;
}
/**
* Helper enum indicating how a file's base64 padding was replaced.
*/
public enum EndingType {
Cr,
CrLf,
None
}
}
The second class is not mine and it should be good, the problem is on the first class [XXXXXXXXXXXX] Thanks!
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|