Question Emil Odobasic · Dec 14, 2023

How can I send a file through Form Data in a REST HTTP Request?

Hello everyone!
I have to build a REST service that receives a POST HTTP-request, collect a file from that request in the Form data and then send it in another HTTP Post request through Form Data. However I can't really seem to send the file, and I do not know where it has gone wrong. All I am getting told is that no file is being received from my HTTP Post request. I am reaching the REST Service I am supposed to send the request to, but nothing is being sent.
I would be really thankful if someone could give some insight why nothing is being sent in my request. Thanks beforehand! :)

So, this is my REST-Service that receives the HTTP request:

XData UrlMap [ XMLNamespace = "https://www.intersystems.com/urlmap]
{
<Routes>
<Route Url="/TEST" Method="POST" Call="PostHandler" />
</Routes>
}

ClassMethod PostHandler() As %Status
{

set vProfile = %request.Get("profile")

set vFilen = %request.GetMimeData("file")

set classToPostHandler = ##class(Classes.PostClass).%New()

set classToPostHandler.Profile = vProfile

set classToPostHandler.file = vFilen

set status = ##class(Ens.Director).CreateBusinessService("PostHandlerService", .instance)
    #dim response as %DynamicObject
    
    set status = instance.OnProcessInput(classToPostHandler, .response)
    
   if $ISOBJECT(response)
{          Do ##class(%REST.Impl).%SetContentType("application/json; charset=utf-8")
       Do ##class(%REST.Impl).%SetStatusCode("200")
       Do ##class(%REST.Impl).%WriteResponse(response)
       Quit $$$OK
}else
{
Do ##class(%REST.Impl).%SetStatusCode("500")
write "ERROR"
}Quit $$$OK

}

Underneath is my class that is used to transfer "vProfile" and "vFilen" to the Business Service that executes the HTTP Post request:

Class Classes.PostClass Extends %Persistent
{

Property Profile As %String;

Property file As %Stream.FileBinary;

}

Underneath here is the Business Service that executes the HTTP Post request:

Method OnProcessInput(pInput As Classes.PostClass, Output pOutput As %RegisteredObject) As %Status
{
set profile = pInput.Profile
set filen = pInput.file 
set request = ##class(%Net.HttpRequest).%New()
set request.Server = "placeholderurl.url"
set request.ContentType = "multipart/form-data;"
Do request.SetHeader("Accept", "*/*")
Do request.SetHeader("Accept-Encoding","gzip, deflate, br")
Do request.InsertFormData("profile", profile)
Do request.InsertFormData("file",filen) 
set tsc = request.Post("placeholderendpoint")


set response = request.HttpResponse.Data
set code = request.HttpResponse.StatusCode
$$$LOGINFO(response)
$$$LOGINFO(code)
set pOutput = response
Quit $$$OK
}else
{
$$$LOGINFO("ERROR")
}
}Quit $$$OK
}

}

Product version: IRIS 2023.3

Comments

David Hockenbroch · Dec 14, 2023

@Marc Mundt's comment here might be helpful. You'll need to instantiate some %Net.MIMPart objects, put them together, and send them.

0
Chris Stewart  Dec 14, 2023 to David Hockenbroch

Having just done the same thing today, following Marc's comment (which is the same code in the documentation for MIMEParts) should get you what you need.

As it stands, I'm pretty sure you are just passing a handle to a stream to the FormData, rather than referencing the content of the stream.  This section of the example will correctly populate the HTTP Request body:

  // Write out Root MIME Element (containing sub-MIME parts) to HTTP Request Body.
    Set writer = ##class(%Net.MIMEWriter).%New()
    Set sc = writer.OutputToStream(tHttpRequest.EntityBody)
    if $$$ISERR(sc) {do $SYSTEM.Status.DisplayError(sc) Quit}
    Set sc = writer.WriteMIMEBody(rootMIME)
    if $$$ISERR(sc) {do $SYSTEM.Status.DisplayError(sc) Quit}
0
Emil Odobasic  Dec 15, 2023 to Chris Stewart

It worked great! Thanks alot for the help! :)

0
Emil Odobasic  Dec 15, 2023 to David Hockenbroch

Yes! This worked perfectly! Thank you so much for the help! :)

0
Enrico Parisi · Dec 14, 2023
set status = instance.OnProcessInput(classToPostHandler, .response)

You are not supposed to call the OnProcessInput() callback method directly, instead the ProcessInput() method should be called.

Sometime calling OnProcessInput() works, sometimes create problems.

Enrico

0
Emil Odobasic  Dec 15, 2023 to Enrico Parisi

I will look into that, thank you! :)

0