Hi Kenneth,
I've create a sample API in SnapDevelop 2021 and a corresponding sample application in PB2019R3 to show how you can create an API that will accept a file and structured data. The API uses a Model (C# Class) that encapsulates the file and the structured data and is the argument to the API controller method. That is the easiest way of passing multiple arguments to an API controller.
The first method will accept a multipart form post which includes the file and the structured data, the second method will accept json which includes the file contents as a base64 encoded string. Base64 encoding a file and embedding it in json is ok if it's relatively small, but I generally don't like to do it. But these are two possible ways you can upload a file with structured data.
For the examples, I'm sending the xml as a string, but in real life you would probably read it from a file into a string. I'm also only passing the doctype structured data from the Powerbuilder examples, but your other structured data (docsubtype, docnumber, docpartysite etc) are handled exactly the same as doctype, either as an additional form-data or json element.
C# Class to capture the data
public class DocumentData
{
public IFormFile XmlFile { get; set; }
public string XmlFileData { get; set; }
public string doctype { get; set; }
public string docsubtype { get; set; }
public string docnumber { get; set; }
public string docpartysite { get; set; }
public string docusername { get; set; }
public string docpassword { get; set; }
}
C# Controller Method to receive a multipart form including a file
[HttpPost]
public ActionResult GetDocumentResponseForm([FromForm] DocumentData documentData)
{
string xml = "";
// This is one way of converting the xml file to a string, but you can do
// whatever you need to do with it (save it to a file etc.).
// Note. If the file is a binary file don't save it to a string. This is just an example!
if (documentData.XmlFile != null && documentData.XmlFile.Length > 0)
{
using (var stream = new MemoryStream())
{
documentData.XmlFile.CopyTo(stream);
stream.Position = 0;
StreamReader reader = new StreamReader(stream);
xml = reader.ReadToEnd();
}
}
// For production do whatever you need to do with the file and data here
// and send back a suitable response
// For testing, echo back the data received to show that it was received correctly
return Ok($"{documentData.doctype}\r\n{xml}");
}
C# Controller Method to receive a base64 encoded file in JSON
[HttpPost]
public ActionResult GetDocumentResponseJson([FromBody] DocumentData documentData)
{
string xml = "";
// Convert the base64 encoded xml data to a string
// Then you can do whatever you want with it (save to a file etc.).
// Note. If the file is a binary file don't save it to a string. This is just an example!
if (!String.IsNullOrEmpty(documentData.XmlFileData))
{
var xmlBytes = System.Convert.FromBase64String(documentData.XmlFileData);
xml = Encoding.UTF8.GetString(xmlBytes);
}
// For production do whatever you need to do with the file and data here
// and send back a suitable response
// For testing, echo back the data received to show that it was received correctly
return Ok($"{documentData.doctype}\r\n{xml}");
}
Powerbuilder Code to send xml as a multipart form to our api. (This code is very similar to Marcin Jurkowski's answer below)
HttpClient lnv_HttpClient
integer li_rc, li_ResponseStatusCode
string ls_ResponseBody, ls_ResponseStatusText
string ls_uri
string ls_boundary
blob lblb_form_data
string ls_xml
string ls_doctype
// Set this to the server, port and path to your api
ls_uri = 'http://localhost:5000/api/document/GetDocumentResponseForm'
// I've just hard coded the xml here as a string, but in real life you would probably read this from a file
ls_xml = '<ADDRESS>Accidently Kelly Street</ADDRESS>'
// For brevity, I've only included doctype as the structured data to send, but you can add all the other structured data similiarly
ls_doctype = 'Test Document'
ls_boundary = '$$$Boundary$$$'
lblb_form_data = blob('--' + ls_boundary + '~r~n', EncodingUTF8!)
lblb_form_data += blob('Content-Disposition: form-data; name="doctype"' + '~r~n~r~n' + ls_doctype + '~r~n', EncodingUTF8!)
lblb_form_data += blob('--' + ls_boundary + '~r~n', EncodingUTF8!)
lblb_form_data += blob('Content-Disposition: form-data; name="XmlFile"; filename="data.xml"~r~nContent-Type: application/xml~r~n~r~n', EncodingUTF8!)
lblb_form_data += blob(ls_xml, EncodingUTF8!)
lblb_form_data += blob('~r~n--' + ls_boundary + '--~r~n', EncodingUTF8!)
lnv_HttpClient = Create HttpClient
lnv_HttpClient.SetRequestHeader("Content-Type", "multipart/form-data; boundary=" + ls_boundary)
li_rc = lnv_HttpClient.SendRequest('POST', ls_uri, lblb_form_data)
// From here down is identical for both methods
if li_rc = 1 then
// obtain the response data
li_ResponseStatusCode = lnv_HttpClient.GetResponseStatusCode()
ls_ResponseStatusText = lnv_HttpClient.GetResponseStatusText()
lnv_HttpClient.GetResponseBody(ls_ResponseBody)
if li_ResponseStatusCode = 200 then
MessageBox('Debug - Response Received', ls_ResponseBody)
else
MessageBox('Debug - Error Calling API', string(li_ResponseStatusCode) + ' ' + ls_ResponseStatusText + '~r~n' + ls_ResponseBody)
end if
else
MessageBox('Debug - Error Sending API Request', 'Error calling API endpoint ' + ls_uri + ' - [' + string(li_rc) + ']')
end if
Destroy (lnv_HttpClient)
Finally some Powerbuilder code which will send xml as a base64 encoded element of a json object.
HttpClient lnv_HttpClient
integer li_rc, li_ResponseStatusCode
string ls_json, ls_ResponseBody, ls_ResponseStatusText
string ls_uri
string ls_xml
string ls_doctype
// Set this to the server, port and path to your api
ls_uri = 'http://localhost:5000/api/document/GetDocumentResponseJson'
// I've just hard coded the xml here as a string, but in real life you would probably read this from a file
ls_xml = '<ADDRESS>Accidently Kelly Street</ADDRESS>'
// For brevity, I've only included doctype as the structured data to send, but you can add all the other structured data similiarly
ls_doctype = 'Test Document'
// An example of how to base64 encode the xml so we can add it to the json
Blob lblb_Xml
string ls_xml_encoded
CoderObject lnv_CoderObject
lnv_CoderObject = Create CoderObject
lblb_Xml = Blob(ls_xml, EncodingANSI!)
ls_xml_encoded = lnv_CoderObject.Base64Encode(lblb_Xml)
Destroy lnv_CoderObject
// Dodgy way of creating the json. Don't do this in real life. You'd probably want to use JsonGenerator
ls_json = '{'
ls_json += '"XmlFileData" : "' + ls_xml_encoded + '",'
ls_json += '"doctype" : "' + ls_doctype + '"'
ls_json += '}'
//ls_json = '{"XmlFileData" : "PEFERFJFU1M+QWNjaWRlbnRseSBLZWxseSBTdHJlZXQ8L0FERFJFU1M+","doctype":"Test Document"}'
lnv_HttpClient = Create HttpClient
lnv_HttpClient.SetRequestHeader("Content-Type", "application/json;charset=UTF-8")
li_rc = lnv_HttpClient.SendRequest('POST', ls_uri, ls_json, EncodingUTF8!)
// From here down is identical for both methods
if li_rc = 1 then
// obtain the response data
li_ResponseStatusCode = lnv_HttpClient.GetResponseStatusCode()
ls_ResponseStatusText = lnv_HttpClient.GetResponseStatusText()
lnv_HttpClient.GetResponseBody(ls_ResponseBody)
if li_ResponseStatusCode = 200 then
MessageBox('Debug - Response Received', ls_ResponseBody)
else
MessageBox('Debug - Error Calling API', string(li_ResponseStatusCode) + ' ' + ls_ResponseStatusText + '~r~n' + ls_ResponseBody)
end if
else
MessageBox('Debug - Error Sending API Request', 'Error calling API endpoint ' + ls_uri + ' - [' + string(li_rc) + ']')
end if
Destroy (lnv_HttpClient)