Uploading a file from MSDyn365BC as “multipart/form-data” message
Yes, uploading a file from Microsoft Dynamics 365 Business Central to a Web API sounds easy because Microsoft AL provides all these new and cool objects like JsonObject, HttpRequest, HttpResponse and HttpClient.
But sometimes the devil is in the detail…
“How-to setup the corret HttpRequest message with all the needed HttpHeaders and how-to add the file content?”
Swagger “Web API” Definition
Good news, my Web API provide a Swagger definition. Now I see what is needed. Swagger-UI visualization display the API method “<host>/api/data”, the HTTP Verb “POST”, and the all parameters.
In simulation mode the Swagger-UI provide the first possibility to test the Web API call:
This part worked well with my file and filename!
Simulating the Web API call
Next step and a better way to set up and test the HTTP calls is a simulation. This allows to repeat the call with reduced clicking like selecting a file from web browser.
Postman or in my case the REST Client extension for Visual Studio Code are useful tools. You are able to specify in detail the required message and you can test this call as well.
Simulated “multipart/form-data” Http-Message
My Web API require a message with “multipart/form-data” and the Http specification for the call with REST Client extension is:
POST http://myapi.com/api/data HTTP/1.1
Content-Type: multipart/form-data; boundary="123456789"
--123456789
Content-Disposition: form-data; name="file"; filename="data.json"
Content-Type: application/json
{ "id": "my json file" }
--123456789--
In this example, the identifier “123456789” is used as a “boundary” to separate multiple content parts of the “multipart/form-data” message.
Further you can see, that the content of the boundary contains own message header definitions to describe the parameters as well as the content of the file.
HttpRequest with “multipart/form-data” in Microsoft AL
The previous created Http-Simulation provide all information about the needed commands in Microsoft AL.
Set up “form-data” content
First task is to set up file content and parameters between the boundary definition “123456789”. The “form-data” information contains boundary start, header with parameters, file-content, and boundary end.
I use these variables:
var tb: TextBuilder;
content: HttpContent;
… and a TextBuilder (similar to StringBuilder in C#), because it is optimized to concatenate text. The needed code in Microsoft AL is:
// Boundary starts
tb.AppendLine('--123456789');
// Content Disposition Header Information and parameters like "filename"
tb.AppendLine('Content-Disposition: form-data; name="file"; filename="data.json"');
tb.AppendLine('Content-Type: application/json');
// Empty line required to separate the header information from payload
tb.AppendLine();
tb.AppendLine('{"id": "my json file" }');
// Boundary ends
tb.AppendLine('--123456789--');
… and the result as text:
--123456789
Content-Disposition: form-data; name="file"; filename="data.json"
Content-Type: application/json
{ "id": "my json file" }
--123456789--
Good warm up!
Set up the HttpMessage
My needed variables for this task in Micorsoft AL are:
var client: HttpClient;
requestMessage: HttpRequestMessage;
responseMessage: HttpResponseMessage;
content: HttpContent;
headers: HttpHeaders;
It needs the URL, HTTP Verb, Http-Header information, and the body content to set up the whole message:
// Setup the URL
requestMessage.SetRequestUri('https://myapi.com');
// Setup the HTTP Verb
requestMessage.Method := 'POST';
// Setup the Header Information like Authorization
requestMessage.GetHeaders(headers);
headers.Add('Authorization', 'Basic base64-user-password');
// Write the message content (multipart/form-data) into the message body
// (Note: This call will set some header information)
content.WriteFrom(tb.ToText());
// update the content header and define the boundary
content.GetHeaders(headers);
if headers.Contains('Content-Type') then headers.Remove('Content-Type');
headers.Add('Content-Type', 'multipart/form-data; boundary="123456789"');
Finally the HttpClient can send the composed HttpMessage and provide the result as HttpResponse (“responseMessage”):
client.Send(requestMessage, responseMessage);
Final Result
The whole method to upload a file as “multipart/form-data” to an Web API in Microsoft AL is:
procedure UploadJsonAsFile(json: Text; filename: Text): Integer
var
client: HttpClient;
requestMessage: HttpRequestMessage;
responseMessage: HttpResponseMessage;
content: HttpContent;
headers: HttpHeaders;
tb: TextBuilder;
responseText: Text;
begin
// Set up the message content for multipart/form-data
tb.AppendLine('--123456789');
tb.AppendLine(StrSubstNo('Content-Disposition: form-data; name="file"; filename="%1"', filename));
tb.AppendLine('Content-Type: application/json');
tb.AppendLine(); // Empty line required to separate the header information from payload
tb.AppendLine(json);
tb.AppendLine('--123456789--');
// Write the content into HttpContent
// (Note: This call will set some header information)
content.WriteFrom(tb.ToText());
// Get Content Headers
content.GetHeaders(headers);
// update the content header information and define the boundary
if headers.Contains('Content-Type') then headers.Remove('Content-Type');
headers.Add('Content-Type', 'multipart/form-data; boundary="123456789"');
// Setup the URL
requestMessage.SetRequestUri('https://myapi.com');
// Setup the HTTP Verb
requestMessage.Method := 'POST';
// Add some request headers like:
requestMessage.GetHeaders(headers);
headers.Add('Authorization', 'Basic base64-user-password');
// Set the content
requestMessage.Content := content;
// Send the message
client.Send(requestMessage, responseMessage);
// Return the Status Code
exit(responseMessage.HttpStatusCode());
end;
Conclusion
This example shows:
- A simulation helps a lot to set up and test the Http-Message.
- The Http-Simulation describes perfect the needed parts (header, body) of the Http-Message.
…and the Microsoft AL implementation looks “simple”.
If you know a better way to uploaded a file as “multipart/form-data” with Microsoft AL, Start Sharing your approach and experience with the community 😉
2 thoughts on “Uploading a file from MSDyn365BC as “multipart/form-data” message”
There is visibly a bunch to realize about this. I believe you made certain good points in features also.
nice artilce , thank you . keep it up
Comments are closed.