How do you access meta-data (form data) sent with a file upload (using enctype="multipart/form-data"
) in a ColdFusion 11 REST Service?
Mocking up a simple service:
component
restpath = "test"
rest = true
{
remote void function upload(
required numeric id restargsource = "Path",
required any document restargsource = "Form",
required string title restargsource = "Form"
)
httpmethod = "POST"
restpath = "{id}/"
produces = "application/json"
{
if ( FileExists( document ) )
restSetResponse({
"status" = 201,
"content" = {
"id" = id,
"title" = title,
"size" = GetFileInfo( document ).size
}
});
else
restSetResponse({
"status" = 400,
"content" = "Nothing uploaded"
});
}
}
and a simple HTML file to test it:
<!DOCTYPE html>
<html>
<body>
<form method="post"
action="http://localhost/rest/TestService/test/1/"
enctype="multipart/form-data">
<input type="text" name="title" value="File Title" /><br />
<input type="file" name="document" /><br />
<button type="submit">Submit</button>
</form>
</body>
</html>
Then the service returns a 500
HTTP status code and the JSON response:
{"Message":"The DOCUMENT parameter to the upload function is required but was not passed in."}
Checking the HTTP request headers shows that both fields were passed in but the service is not receiving them.
Commenting out the enctype
attribute within the HTML form (so it uses the default encoding of application/x-www-form-urlencoded
) then the service returns a 400
HTTP status code and the response Nothing uploaded
.
How can this be resolved?
To be thoroughly confusing the request data populates the FORM
variable but, even though the function arguments state restargsource="form"
, the value is not passed on to the REST service as its argument when the enctype
is anything other than application/x-www-form-urlencoded
.
This is confirmed in Adobe's Getting started with RESTful web services in ColdFusion article:
Form parameters:
In many scenarios, you must process data that you enter in the form in a REST service. You can use this data to make a new entry (
POST
) or update an existing record (PUT
) in the database. If you use form fields, setrestargsource
toform
. This extracts the data present in the request and makes it available for further processing. Also, you must set the content-type header toapplication/x-www-form-urlencoded
when sending data as form fields.
However, it can be solved by changing the arguments which would use restargsource="form"
to be parameters explicitly scoped within the form
variable.
So, the modifying the service like this:
component
restpath = "test"
rest = true
{
remote void function upload(
required numeric id restargsource = "Path"
)
httpmethod = "POST"
restpath = "{id}/"
produces = "application/json"
{
param name="form.document" type="string";
param name="form.title" type="string";
if ( FileExists( form.document ) )
restSetResponse({
"status" = 201,
"content" = {
"id" = id,
"title" = form.title,
"size" = GetFileInfo( form.document ).size
}
});
else
restSetResponse({
"status" = 400,
"content" = "Nothing uploaded"
});
}
}