I am attempting to upload a file from a React frontend to a Scala/Play Framework backend and it is not working.
So I have a button that uploads a file and calls handleUpload
:
handleUpload=()=>{
if(this.state.file!=null){
console.log('value of this.state.file: ', this.state.file)
var dataSend = new FormData();
dataSend.append('file_upload', this.state.file);
console.log('value of dataSend before send: ', dataSend);
this.setState({lastCommand: 'file_upload'}, ()=>{
this.props.sendRequest({
url: "http://localhost:9000/upload",
method: "post",
multipart: true,
data:{
payload:{
data: dataSend
}
}
});
})
}
}
this.props.sendRequest
is in my actions (Redux) and is the following:
export const request = payload => {
return function(dispatch) {
console.log("value of payload in actions request: ", payload)
var config = {}
if (payload.method=="post"||payload.method=="patch"){
config = {
url: payload.url,
method: payload.method,
withCredentials: true,
data: payload.data,
}
if('multipart' in payload && payload.multipart){
config.headers = { 'Content-Type': 'multipart/form-data' }
}
}else if (payload.method=='get'){
config = {
url: payload.url,
method: payload.method,
withCredentials: true,
}
}
console.log('value of config before send in actions/request: ', config);
axios(config)
.then(response=>{
return dispatch({
val: response,
type: (payload.method=="post"||payload.method=="patch")?"POST_RETURN_SUCCESSFUL":"GET_RETURN_SUCCESSFUL",
sentID: ('sentID' in payload)?payload.sentID:null
})
})
.catch(error=>{
console.log('there was an error in request return in actions', error);
return dispatch({
val: error,
type: (payload.method=="post"||payload.method=="patch")?"POST_RETURN_ERROR":"GET_RETURN_ERROR",
sentID: ('sentID' in payload)?payload.sentID:null
})
});
};
}
Here is the value of the config as console logged above:
value of config before send in actions/request:
{…}
data: {…}
payload: {…}
data: FormData { }
<prototype>: Object { … }
<prototype>: Object { … }
headers: {…}
"Content-Type": "multipart/form-data"
<prototype>: Object { … }
method: "post"
url: "http://localhost:9000/upload"
withCredentials: true
Note that while it appears that the form data is empty this is an artifact of the inability of the web console to display it (How to inspect FormData?)
This should be hitting the following route in my play framework (as defined in my config/routes
file):
+nocsrf
POST /upload controllers.PostController.admin_upload
where +nocsrf
is simply a handler to prevent cross site request forgery.
NOTE: I am sending to the url "http://localhost:9000/upload" on the frontend.
My upload function controllers.PostController.admin_upload
is a simple hello_there
def admin_upload[T]:Action[AnyContent] = Action.async {request: Request
[AnyContent] => Future {
println("****************************")
println("inside admin_upload")
println("****************************")
var responseVal = new Response().standard_response("/admin/patch", "DUMMY - STILL WORKING ON ROUTE")
Ok(responseVal)
}(ec)
}
I get no console logging on the backend, and I get a 400 NOT FOUND
error on the frontend.
Here is my network request headers:
Host: localhost:9000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data
Content-Length: 23
Origin: http://localhost:3000
Connection: keep-alive
Referer: http://localhost:3000/admin
Cookie: PLAY_SESSION=SUPERDOOPERSECRETDAWG
and response headers:
HTTP/1.1 400 Bad Request
Vary: Origin
Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Credentials: true
X-Permitted-Cross-Domain-Policies: master-only
Date: Wed, 13 Nov 2019 17:08:34 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 1170
I am totally lost, this should work.
What is broken?
EDIT:
If I drop the Content-Type
header on the axios request and console out the result I do get inside the admin_upload
function but the request body is of course empty because I haven't sent a form. So I've narrowed the problem to "how do I send the correct content-type flag to play framework?"
****************************
inside admin_upload
****************************
value of request.body:
AnyContentAsJson({"payload":{"data":{}}})
request.body.asMultipartFormData
None
EDIT EDIT:
Even the most simple example as shown from here: https://www.playframework.com/documentation/2.7.x/ScalaFileUpload
Doe not appear to work.
def admin_upload = Action(parse.multipartFormData) { request =>
println("****************************")
println("inside admin_upload")
println("****************************")
println(request.body)
var responseVal = new Response().standard_response("/admin/patch", "DUMMY - STILL WORKING ON ROUTE")
Ok(responseVal)
}
EDIT EDIT EDIT:
OK - this does work in Insomnia, I had it configured incorrectly. This means that the issue is that axios is not working correctly, I'll send a bug report to them.