You can receive multipart form values and uploaded files whose names and count are not known when you design a ViewModel, by collecting them in two named multilinks in a REST-enabled Turnkey ViewModel.
Use this pattern when an external editor, form builder, or integration posts arbitrary form fields or files. For example, an online texture editor may post several document files, while the file field names vary between requests.
Before you start
Expose the ViewModel as a REST service as described in HowTos:Expose REST Service. The ViewModel must have the tagged value RestAllowed set to true.
Send the request to the Turnkey REST Post endpoint. A REST Get endpoint does not apply posted values. For endpoint formats, root-object IDs, access control, and ordinary known columns, see HowTos:Expose REST Service.
Model the collectors
Add either or both of the following mutable multilinks to the root ViewModel class. A mutable multilink is a link that Turnkey can add objects to during the POST.
| Root ViewModel multilink | Required attributes on each linked object | Receives |
|---|---|---|
FormFields
|
Key and Value, both string attributes
|
Form values whose field names do not match a named ViewModel column |
FormFiles
|
Name, FileName, ContentType as string attributes, and Bytes as Blob or Blob?
|
Uploaded multipart files |
The multilink and attribute names are part of the contract. Use the exact spelling shown above.
For example, a root ViewModel for an import endpoint can contain:
- A normal column named
Attribute1. - A mutable multilink named
FormFieldsto a class withKeyandValue. - A mutable multilink named
FormFilesto a class withName,FileName,ContentType, andBytes.
How Turnkey maps a multipart POST
When Turnkey receives a multipart form POST, it first recognizes values that match named ViewModel columns. Remaining form values can be collected through FormFields.
| Posted part | Result |
|---|---|
A form value named Attribute1, when the ViewModel has a column named Attribute1
|
Turnkey applies the value to the Attribute1 ViewModel column. It does not add this value to FormFields.
|
A form value named thename, when no named ViewModel column matches it
|
Turnkey adds a new object to FormFields, with Key = 'thename' and Value set to the posted string value.
|
A file part named thename with uploaded filename somefilename.txt
|
Turnkey adds a new object to FormFiles. Name is the posted part name, FileName is the uploaded filename, ContentType is the file content type when supplied, and Bytes contains the file bytes.
|
Turnkey does not merge received values with existing FormFields or FormFiles objects. Each POST adds new linked objects. Plan to process, move, or clear these objects if the root object is reused.
Process received values with actions
Place root-level actions in the ViewModel to control processing order. REST Post actions placed before the root attributes run before posted data is applied; actions placed after the attributes run after the data is applied.
Use this order when you need to remove previous collected data before accepting a new request, then interpret the received records afterwards:
- Add an action before the root attributes to clear or prepare the existing collector links.
- Add the normal columns and the
FormFields/FormFilesmultilinks. - Add an action after the root attributes to inspect the new linked objects, validate keys, move file bytes, or create modeled business objects.
For example, an action after the columns can examine each FormFields object to decide whether an unknown key is supported, and examine each FormFiles object before retaining its Bytes value.
Example multipart request in C#
The following request posts one known form value, one unknown form value, and two files. It targets a ViewModel named TestRestPost rooted at 1!0.
MemoryStream ms = new MemoryStream();
ms.Write(new byte[10] { 1, 2, 3, 4, 45, 5, 6, 7, 8, 9 }, 0, 10);
ms.Position = 0;
HttpClient c = new HttpClient();
var content = new MultipartFormDataContent();
content.Add(new StringContent("Ticks:" + DateTime.Now.Ticks.ToString()), "Attribute1");
content.Add(new StringContent("hello"), "thename");
content.Add(new ByteArrayContent(new byte[10] { 1, 2, 3, 4, 45, 5, 6, 7, 8, 9 }, 0, 10), "thename", "somefilename.txt");
var streamcontent = new StreamContent(ms);
streamcontent.Headers.ContentType = new MediaTypeHeaderValue("text/html");
content.Add(streamcontent, "thename3", "thefilename.WithExtension");
var res = c.PostAsync(
"http://localhost:5052/TurnkeyRest/Post?command=TestRestPost&id=1!0",
content).Result;
With the example ViewModel structure, this request produces the following result:
Attribute1receives theTicks:...value because it is a known ViewModel column.- One
FormFieldsobject is added withKey = 'thename'andValue = 'hello'. - One
FormFilesobject is added forthename, withFileName = 'somefilename.txt'. - A second
FormFilesobject is added forthename3, withFileName = 'thefilename.WithExtension'andContentType = 'text/html'.
For a reusable C# request pattern, including the multipart boundary requirement, see Documentation:Use c-sharp code to post to TurnkeyRest.
Related request content types
This collection pattern is for multipart form data. For a POST body that contains string content rather than form data, Turnkey can place the content in a column named STRINGCONTENT, if that column exists. For binary content that is not posted as form data, Turnkey can stream the bytes to a blob column named BYTEARRAYCONTENT, if that column exists.
If your client sends JSON and you want to create or update modeled objects from it, use the JSON processing approach described in HowTos:Expose REST Service rather than modeling arbitrary values as form fields.
