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.
The solution was to make a cut out in my request handler and use fetch
instead of axios
for file uploads. Like this:
export const request = payload => {
return function(dispatch) {
console.log("value of payload in actions request: ", payload)
var config = {}
if('multipart' in payload && payload.multipart){
config = {
method: "POST",
body: payload.data
}
fetch("http://localhost:9000/upload", config)
.then(response => response.json())
.then(response => {
console.log("inside success in fetch and value of response: ", response)
return dispatch({
val: response,
type: "POST_RETURN_SUCCESSFUL"
})
})
.catch(error =>{
console.log('there was an error in request return in actions', error);
return dispatch({
val: error,
type: "POST_RETURN_ERROR"
})
});
}else{
if (payload.method=="post"||payload.method=="patch"){
config = {
url: payload.url,
method: payload.method,
withCredentials: true,
data: payload.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
})
});
}
};
}
Axios has problems an issue with file uploads. See here: https://github.com/axios/axios/issues/318
The workaround then is to use fetch.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.