Forums

400 Bad request. The browser (or proxy) sent a request that this server could not understand.

Hi all! I have to upload on /static/files an image in my Flask app, but I recieve the 400 Bad request error. I drag and drop the image on a div and fire the function loadDoc (javascript side) passing a File object as argument (imgfile).

Then I create a formData object and append 2 fields, the first one is an identifier, the second one is the image itself.

When I start the ajax request the server refuses my request. Did I forgot anything? Is it necessary a specific header for Pythonanywhere webserver?

Thank you in advance for your support.

Fabio

Javascript side:

function loadDoc(id_department, imgfile) {    
  objectURL = URL.createObjectURL(imgfile);
  var formData = new FormData();
  formData.append("id_department", id_department);
  formData.append("file", imgfile);
  var xhttp = new XMLHttpRequest();
  xhttp.open("POST", "/sendFiles", true);
  xhttp.setRequestHeader('Content-Type', 'multipart/form-data; boundary=AaB03x;  charset=utf-8');
  xhttp.send(formData);
  return xhttp;
}

Flask side:

@app.route('/sendFiles', methods=['POST'])
@login_required
def sendFiles():
    id_dipartimento = request.values["id_dipartimento"]
    uploaded_files = request.values["file"]
--- other stuff here.... ---

Are there any hints in your error log?

Also see if something like this helps.

Hi Conrad!

I'm sorry but little hints came from logfiles: error.log and server.log, didnt'show any error, access.log showed this one:

87.9.232.61 - archeo [25/Jul/2016:13:40:20 +0000] "POST /sendFiles HTTP/1.1" 400 192 "https://fabioquintilii.pythonanywhere.com/plancia" "Mozilla/5.0 (X11; Linux i686; rv:47.0) Gecko/20100101 Firefox/47.0" "87.9.232.61"

The other link You gave me pointed to a mysql topic, I think it isn't helpful to solve my issue...

Hi Fabio -- just in case it's not clear, there's nothing PythonAnywhere-specific here, or at least there shouldn't be. Do you have Flask debugging switched on? It should be able to give more guidance about why it's rejecting the post. One possibility -- perhaps there's a CSRF check going on and it's rejecting it because you're not passing the correct value back for that?

" perhaps there's a CSRF check going on"

Hi Giles, this is a good question, the CSRF is enabled and till now it worked fine, but this form is dynamically created in javascript, maybe it needs to add a field with CSRF value?

About Flask debugging I'm pretty sure it's switched off, when I work offline on my desktop i set debug=True when i call app.run() un the Controller, can I do the same here? Just add the lines:

if __name__ == "__main__":
  app.run(debug=True)

at the bottom of the file?

Thank you!

app.config['DEBUG'] = True or just app.debug = True would work.

If you do have csrf protection turned on, then it does seem like you would need to include the csrf token in any js posts.

Neither CSRF protection worked, I set it according to this page. I have to find something else.

Could you share the JavaScript code you used to do the CSRF stuff?

Sure1

CSRF

function loadDoc(id_dipartimento, imgfile) {

  var meta = document.getElementsByTagName("meta");
  for(var i=0; i< meta.length; i++)
  {
      if(meta[i].getAttribute('content') != "")
      {
        var csrftoken = meta[i].getAttribute('content');
      }
  }
  var formData = new FormData();
  formData.append("id_dipartimento", id_dipartimento);
  formData.append("file", imgfile);
  var xhttp = new XMLHttpRequest();
  xhttp.open("POST", "/sendFiles", true);
  xhttp.setRequestHeader('Content-Type', 'multipart/form-data; boundary=AaB03x;  charset=utf-8');
  xhttp.setRequestHeader("X-CSRFToken", csrftoken)
  xhttp.send(formData);
  return xhttp;
}

I tried also with Jquery **WITHOUT the debuggger returned 400 error, status ABORTED

function sendFiles(id_dipartimento, imgfile)
{

    var formData = new FormData();
    formData.append("id_dipartimento", id_dipartimento);
    formData.append("file", imgfile, imgfile.filename);
    return $.ajax({
            type        : 'POST',
            url         : "/sendFiles",
            enctype     : 'multipart/form-data',
            processData : false,  // tell jQuery not to process the data
            contentType : false,   // tell jQuery not to set contentType
            data        : formData
    });
}

It looks like you're missing part of the filter code from the page you linked to earlier -- they have:

var csrftoken = $('meta[name=csrf-token]').attr('content')

Your code doesn't filter on name=csrf-token.

Hello, After several attempts, got which was the issue, but the solution seems hard to find. I modified Javascript to set ALL headerd necessary to send data via FormData(), but Flask gave the same error, 400 bad request. But I was sure data was transferred to the server. I tried to look into request method and I discovered that Flask used request.data method instead of request.form, and data was converted into a string type. I read that this happens when Flask does not recogize Content-type headers.

Is there any way to force data to fill request.form inestead of request.data?

Thank you!

//////////////////////////////////////////////////////////////////// JAVASCRIPT

function loadDoc(id_dipartimento, imgfile) {

  var meta = document.getElementsByTagName("meta");
  for(var i=0; i< meta.length; i++)
  {
      if(meta[i].getAttribute('content') != "")
      {
        var csrftoken = meta[i].getAttribute('content');
      }
  }

  var formData = new FormData();
  formData.append("id_dipartimento", id_dipartimento);
  formData.append("imgfile", imgfile);

  var xhttp = new XMLHttpRequest();
  xhttp.open("POST", "/sendFiles", true);
  xhttp.setRequestHeader('Content-Type', 'application/octet-stream');
  xhttp.setRequestHeader('enctype','multipart/form-data');
  xhttp.setRequestHeader("X-CSRFToken", csrftoken);
  xhttp.setRequestHeader('X-Upload-Content-Length', imgfile.size);
  xhttp.setRequestHeader('Content-length', imgfile.size);
  xhttp.setRequestHeader('Content-transfer-encoding', 'binary');
  xhttp.send(formData);
  return xhttp;
}

//////////////////////////////////////////////////////////////////////////////////////// PYTHON

@app.route('/sendFiles', methods=['POST'])
@login_required
def sendFiles():
    print request.data.keys()

//////////////////////////////////////////////////////////////////////////////////////// SERVER.LOG

2016-07-31 10:05:29 -----------------------------1675367026141005455785498554#015#012Content-Disposition: form-data; name="id_dipartimento"#015#012#015#01210#015#012-----------------------------1675367026141005455785498554#015#012Content-Disposition: form-data; name="imgfile"; filename="knife1.jpeg"#015#012Content-Type: image/jpeg#015#012#015#012����
2016-07-31 10:05:29 �#017ML%flk��2)��t
2016-07-31 10:05:29 �����h4/,C����@�#020#020.Ӗ$�#002#030#022��#026#026Ƒ
2016-07-31 10:05:29 Ѐ��� �#010#011v�#004cH!#005�#004  li#024##026@!#005�����#004#021�#030 lE�#030#004#025#020@H��5��#032�#031TdD�#024��@"5�[#031�3B�[F��DvM�, �8�#004F�4(�#004F�#011#021�M�DA"4%cLB��Da#022����Q#022�#033iDNhU��eZvP#026���.Y̠Z]ZX#023*#004#010@B��',I#024#004 %�#010#010[#032D#002#020#022#001#010#010[#032E��#026@!#001#003e"���)D,�06U �C#002#010�X#033(�#010�Y#022BR��#026�o#003#026h7)�3��C��#036�!nqF�r����#033��#014���{#007J�8SA�[�`�#017��S;�'W#020�#016�np#007#016���0N#035�#033�g�d�Խ�h���#016#035�#033�ez��?�ϢL?HN,U�6#024#026AU�h#002/�#034�*#016���#017o#033N���Ǹ�N#031�#037�ez��?�����#013U/�#002q%�uA���G�Q��@���5&��#032�q��Mfˊ5#0230��0`�4���"�#026a�#013���[��Ǹ��g�)�����Vx.9Iz]1#015��z#026UE
2016-07-31 10:05:29 
2016-07-31 10:05:29 4�™SS~��:{����ܬl/#021����#035"��P3�ZMIX���hE0�#020A�J��#015��c��#036�#033՟���/՟���6�8���
2016-07-31 10:05:29 #015��S;�'5#017c��[8ܭ0֋r�������a.#025nq��r���(+x#031��#017��#022������ܭd|ycJܒI$rI$�#030I$$I1$LbZU��1i-$�#030ZI&1V���Ĵ��IKI$�#030I$$I1$LbI$�#030I$$I1���#015
2016-07-31 10:05:29 -----------------------------1675367026141005455785498554--#015

That sounds like something you will have to work out on the js side. Or alternatively, in your flask code, get the .data instead of .form.

I see, but in both cases there are difficulties: on the js side I cannot understand if is necessary to add or change headers, on the python side I tried to access to request. data but it returns a string type. In the code above "print request.data.keys()" raises an exception, that string object has no keys property.

Is there any way to parse or convert it to another format e.g. JSON?

thanks!

How about converting the string to json using the json library? (part of python standard library)