I'm developing a project with TMS Web Core and need to make a POST request to my API to send an image. My API requires the request body to be in multipart/form-data format, so I can't use JSON.
I've tried using the TJSFormData object to build the request body, as recommended for this type of task. However, I can't find the TJSFormData class in any of my project's libraries or units, which prevents me from adding the form fields and the image file to attach them to the TWebHttpRequest parameters.
Could you tell me the unit or library where the TJSFormData object is located, or failing that, what is the correct method to send form-data with an image to my API using TWebHttpRequest?
At this moment there isn't a built-in Pascal class to automatically create the data stream required for multipart form data.
The fastest way to integrate this in a TMS WEB Core app is by reusing & embedding the equivalent JavaScript code in an ASM block.
Doing this in JavaScript is described in this article for example:
Quering for some sample code, this came up:
/**
* POST text fields + files/images as multipart/form-data (BROWSER ONLY).
*
* @param {Object} opts
* @param {string} opts.url - Endpoint to POST to.
* @param {Record<string, string|number|boolean|null|undefined>} [opts.fields]
* Key–value text fields. Non-strings are JSON.stringified.
* @param {Array<{ name: string, file: File|Blob, filename?: string }>} [opts.files]
* Files or images to attach.
* @param {Record<string,string>} [opts.headers] - Extra headers (do NOT set Content-Type).
* @param {AbortSignal} [opts.signal] - Optional AbortController signal.
* @returns {Promise<Response>} - Fetch Response.
*/
export async function postMultipartBrowser({ url, fields = {}, files = [], headers = {}, signal } = {}) {
if (!url) throw new Error("url is required");
const fd = new FormData();
// Text fields
for (const [key, val] of Object.entries(fields)) {
if (val === undefined || val === null) continue;
fd.append(key, typeof val === "string" ? val : JSON.stringify(val));
}
// Files/images
for (const item of files) {
if (!item?.name || !item?.file) throw new Error("Each file must have { name, file }");
const filename =
item.filename ||
(item.file instanceof File && item.file.name) ||
"upload.bin";
fd.append(item.name, item.file, filename);
}
// Let the browser set the correct multipart boundary; don't set Content-Type yourself.
return fetch(url, {
method: "POST",
body: fd,
headers,
signal,
});
}
Okay, I understand that I should create a .js file containing the JavaScript function that will execute the request, and in my Pascal procedure, create an ASM code block to call the JavaScript function.
The only question regarding this is, if I want to call the function within my ASM code, can I import the .js file with the function I need to call as another unit in my .pas, or do I have to import it within the ASM block, as JavaScript ( import { postMultipartBrowser } from './postMultipartBrowser.js';)