简体   繁体   English

播放框架文件上传不起作用(React/Scala)

[英]Play Framework File Upload Not Working (React/Scala)

I am attempting to upload a file from a React frontend to a Scala/Play Framework backend and it is not working.我正在尝试将文件从 React 前端上传到 Scala/Play Framework 后端,但它不工作。

So I have a button that uploads a file and calls handleUpload :所以我有一个上传文件并调用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: this.props.sendRequest在我的操作(Redux)中,如下所示:

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? )请注意,虽然表单数据似乎为空,但这是 web 控制台无法显示它的伪影( 如何检查 FormData?

This should be hitting the following route in my play framework (as defined in my config/routes file):这应该在我的游戏框架中达到以下路线(在我的config/routes文件中定义):

+nocsrf
POST    /upload        controllers.PostController.admin_upload

where +nocsrf is simply a handler to prevent cross site request forgery.其中+nocsrf只是一个防止跨站点请求伪造的处理程序。

NOTE : I am sending to the url " http://localhost:9000/upload " on the frontend.注意:我发送到前端的 url http://localhost:9000/upload

My upload function controllers.PostController.admin_upload is a simple hello_there我上传的 function controllers.PostController.admin_upload是一个简单的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.我在后端没有控制台日志记录,并且在前端收到400 NOT FOUND错误。

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.如果我在 axios 请求中删除Content-Type header 并控制台输出结果,我确实会进入admin_upload function,因为我没有发送表单,因为我当然没有发送请求正文。 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即使是最简单的示例,如下所示: 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.好的 - 这在 Insomnia中确实有效,但我的配置不正确。 This means that the issue is that axios is not working correctly, I'll send a bug report to them.这意味着问题是axios无法正常工作,我将向他们发送错误报告。

axios 坏了

The solution was to make a cut out in my request handler and use fetch instead of axios for file uploads.解决方案是在我的请求处理程序中进行剪切,并使用fetch而不是axios进行文件上传。 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. Axios 存在文件上传问题。 See here: https://github.com/axios/axios/issues/318见这里: https://github.com/axios/axios/issues/318

The workaround then is to use fetch.解决方法是使用 fetch。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM