[Coldbox 6.3.0] AJAX - File Upload

Bit of a strange one. Hopefully something really stupid…!
I am POSTing a file upload using jQuery - FormData to an event handler. This works on my local machine. And it works on my server - apart from nothing is returned. The AJAX POST never completes - “CAUTION: request is not finished yet”. Additionally, a file of 7KB works, but any bigger fails.
The event handler definitely runs and everything server side looks ‘completes’. The last part of the event handler is to create a log file containing the file upload details, rc, etc. which is created OK. The last line is:

event.renderData( data=result, formats=“json” );

And I’ve also tried…

return SerializeJSON(result);

All other AJAX POSTs are fine.
The server is Windows Server 2016. [I have other Lucee (non-Coldbox) applications with AJAX uploads working on this server, so I don’t think the problem is IIS related.]

Any suggestions? I’ve uploaded a screenshot of the event handler


Usually when I return JSON, I use:
event.renderData(type="JSON", data=result);

Reference.: Rendering Data - ColdBox HMVC Documentation

Thanks for the reply, Ancient_Programmer.
I tried your suggestion, same result.

I’ve also tried
event.setView(“main/upload”);
…and then constructing a reply (old skool) in the cfm.

I’ve also tried a new app build.

More infuriating still, everything works when running on a browser on the server (using the same URL). In every case, the IIS logs show the main/upload page getting a 200 response - something else is happening…

I have got an IIS server standard response by using:
event.renderData(
data = “Unauthorized Access: blah blah blah”,
statusCode = “400”,
statusText = “Unauthorized”
)
I can change the statusCode to anything apart from in the 200s. In these cases the request never completes.

Also, if I add the following to the web.config…

<system.webServer>
       <httpErrors existingResponse="PassThrough" />
</system.webServer>

…the request goes back to never completing regardless of the status code.

I have now tried removing renderData - i.e. it just falls through to the default view and the default layout. This works! But instead of JSON, I’ve got an html page to extract my result from - which is expected.

It does seem like there’s a renderData/setView issue with this type of POST.

For completeness I’ve also got cbauth, cbsecurity and cborm installed.

I’ve sort of cracked it.
Despite dataType:“json”, renderData seems to think this is an HTML request. So this response works:

event.renderData( data=prc.result, formats=“html,json” );

I don’t know how renderData works but this has been very tricky to track down!

John, I’m curious why you are setting contentType: false in your $.ajax() call. Can you remove that and see what result you get? Looking at the docs, the contentType may be overriding your type argument:

https://coldbox.ortusbooks.com/the-basics/event-handlers/rendering-data#event-renderdata

Hi Michael, for specifically AJAX file uploads, the contentType MUST be false.

“…When one sets the contentType option to false, it forces jQuery not to add a Content-Type header, otherwise, the boundary string will be missing from it. Also, when submitting files via multipart/form-data, one must leave the processData flag set to false, otherwise, jQuery will try to convert your FormData into a string, which will fail…”

I also forgot to mention that I missed a trailing slash on the URL, which I think is important.

var url = “/Main/upload/”;

1 Like

Ok - as long as jQuery isn’t setting some odd Content-Type header, then. I couldn’t find/didn’t understand the jQuery docs on that option when I looked yesterday. :slight_smile:

But of course today, it now doesn’t work. Argh!

To say I’m struggling to get any sort of reliability/consistency is an understatement. After going through multiple iterations, both my JS [checked within the browser in case of caching] and handler are back to where they started. It worked - JSON response. Then I restarted Lucee…and I’m back to no response.

My current thinking is that the only reliable way to get this to work is to not use setView or renderData, to allow the request to drop through to the default view and layout and then use JS to parse through the resultant HTML. Not ideal, but seems consistent.

The dataType:“json" option in your $.ajax is doing nothing for you that I am aware of, since you’ve also set contentType: false.

Here is the problem as I believe I see it:

  1. You have told ColdBox that you would like to support both html-formatted and json-formatted responses
  2. You have not specified exactly which format to use for this particular request.

I suggest either experimenting with sending a contentType: "application/json" from your $.ajax call - start by removing contentType: false - or explicitly set a type="json" in the renderData() call as @Ancient_Programmer suggested. I am definitely curious if contentType: false is really necessary to prevent jQuery’s $.ajax() from borking the file upload.

Just be aware that formats and type are two different parameters and they do different things. Bottom line, you are not getting a JSON response because you haven’t asked for one. :slight_smile:

Test 1:
contentType:"json"
event.renderData( data=prc.result, formats="json,html" );
File upload: failed
Result: No response

Test 2:
contentType:"application/json"
event.renderData( data=prc.result, formats="json,html" );
File upload: failed
Result: No response

Test 3:
contentType:false
event.renderData( data=prc.result, formats="json,html", type="json" );
File upload: success
Result: Yes - falls through to the default view/layout.

Test 4:
contentType:false
event.renderData( data=prc.result, formats="json,html" );
File upload: success
Result: Yes - but this is the result that seems to be inconsistent…as proven by

Test 5: (different file uploaded)
contentType:false
event.renderData( data=prc.result, formats="json,html" );
File upload: success
Result: No response

…followed by a repeat of test 4, Result: no response!

Test 6:
contentType:false
event.renderData( data=prc.result, type="json" );
File upload: success
Result: Yes - but like tests 4 & 5, this was not repeatable / reliable / consistent.

contentType:false seems to be definitely required, something to do with the “boundary string”.

This is the successful upload request header:
Upload3

And this is the unstructured data submitted in a failed upload (contentType:"application/json" or contentType:"json" ):
Upload4

And the structure of a successful upload (contentType:false )
Upload5

Could it be that multipart/form-data is not recognised by renderData?

I’m curious: why are you setting formats="json,html"? Why would you need the option for an HTML response? I’m curious why you are using this formats param at all when you clearly only want to send a JSON response?

Could it be that multipart/form-data is not recognised by renderData?

Yes, renderData() does nothing with a multipart/form-data content-type header, because it does not translate directly to a “use this response format”. That’s why you need to set type="json" as mentioned above.

If you are not achieving 100% consistent success with event.renderData( data=prc.result, type="json" );, then I would try debugging those failures. If there is no response, why? Is there an error? Make sure your exception handlers are configuring and working. Try taking a look at the server logs in CommandBox.

Not sure what’s going on, just trying to work through this. Good luck!

Additionally, a file of 7KB works, but any bigger fails.

This may be an nginx or IIS or Undertow server configuration issue. I’m too lazy to look that one up.