I am trying to build Multipart Form Data directly in Javascript in order to send my data to a server. I know there are Ajax form plugins, but I really think they wont suit my needs as I will create binary data in the browser and send it as if it were a file submit (The server I will post to requires it that way).
My problem now is that the simplest example of building text Multipart MIME data fails on the server side with an error:
500 Internal Server Error: Invalid boundary in multipart form
I have tried to reduce the code to a bare minimum: In this main.html (this is the name it will be refered to later in the server code) , there are both an html form to submit text the html-non-Ajax way and also a Javascript function which tries to replicate that with XmlHttprequest:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Posting MIME Multipart directly in Javascript</title>
<script>
function sendMimeMultipart(url, data) {
boundary = '---------------------------1504702169761927311267328916'
xhr = new XMLHttpRequest();
xhr.open("POST", url);
//Build the MIME POST request.
var body = "--" + boundary + "\r\n";
body += 'Content-Disposition: form-data; name="contents"\r\n\r\n';
body += data+"\r\n";
body += "--" + boundary + "--"+"\r\n";
var fileSize = body.length
xhr.setRequestHeader("Content-Type", "multipart/form-data, boundary="+boundary);
xhr.setRequestHeader("Content-Length", fileSize);
xhr.send(body);
return true;
}
function sendData() {
sendMimeMultipart('http://localhost:8080/myhandler', "Hello World!");
}
</script>
</head>
<body onload='sendData()'>
<form action = "myhandler" method = "post" enctype = "multipart/form-data">
<input type = "text" name = "contents">
<input type = "submit">
</form>
</body>
</html>
This is the Request object that arrives to the server when using the form:
Request: POST /myhandler
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Language: es-es,es;q=0.8,en-us;q=0.5,en;q=0.3
Connection: keep-alive
Content-Length: 187
Content-Type: multipart/form-data;
boundary=---------------------------18171295601131570933197493099
Host: localhost:8080
Keep-Alive: 115
Referer: http://localhost:8080/
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; es-ES; rv:1.9.2.20)
Gecko/20110803 Firefox/3.6.20
-----------------------------18171295601131570933197493099
Content-Disposition: form-data; name="contents"
Hello World!
-----------------------------18171295601131570933197493099--
And this the Request object arriving to the server when using the Javascript function (sendMimeMultipart):
Request: POST /myhandler
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Language: es-es,es;q=0.8,en-us;q=0.5,en;q=0.3
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 185
Content-Type: multipart/form-data; charset=UTF-8,
boundary=---------------------------1504702169761927311267328916
Host: localhost:8080
Keep-Alive: 115
Pragma: no-cache
Referer: http://localhost:8080/
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; es-ES; rv:1.9.2.20)
Gecko/20110803 Firefox/3.6.20
-----------------------------1504702169761927311267328916
Content-Disposition: form-data; name="contents"
Hello World!
-----------------------------1504702169761927311267328916--
The difference of 2 bytes in Content-Length is because the browser generates the boundaries randomly, being sometimes longer and sometimes shorter. In this case it is one character longer, what accounts for the two byte difference in the two boundary occurrences.
I dont think the server has much to do with this, bus just in case I post the server side code. It is an Appengine snippet intended only for localhost usage; the call to "localhost:8080/myhandler" retrieves the value of "contents" posted by the browser and stores it in a global variable. After that, a call to "localhost:8080/show" displays the text previously retrieved. As I mentioned before, if we send the data using the form, the text content is correctly saved and the "show" handler displays it. If however we use the Javascript, the line of code:
contents = self.request.get("contents")
In MyHandler (code below), produces the error.
Here is the server code:
import cgi
import datetime
import logging
import os
from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.api import images
from google.appengine.ext.webapp import template
from os import environ
contents=''
class mein(webapp.RequestHandler):
def get(self):
template_values = {}
path = os.path.join(os.path.dirname(__file__), 'templates/main.html')
self.response.out.write(template.render(path, template_values))
class MyHandler(webapp.RequestHandler):
def post(self):
global contents
contents = self.request.get("contents")
class Show(webapp.RequestHandler):
def get(self):
global contents
self.response.headers['Content-Type'] = "text/plain"
self.response.out.write(contents)
application = webapp.WSGIApplication([
('/', mein),
('/myhandler', MyHandler),
('/show', Show)
], debug=True)
def main():
run_wsgi_app(application)
if __name__ == '__main__':
main()
Any idea of why this should be failing? I have tried a zillion different things, but I dont seem to be able to make it work or to understand the reason why it doesnt!.
Thanks very much in advance for your ideas and help.
All the best:
-Javier
I encountered the same error message when trying to construct a http file upload manually. I got it to work by replacing the comma(,) with a semicolon(;) in the Content-Type headers. In your case, by replacing:
xhr.setRequestHeader("Content-Type", "multipart/form-data, boundary="+boundary);
with:
xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary="+boundary);
This seems to be related to the Python backend, because I had this same problem with Django(Python) and when I debugged it against a PHP test server both comma and semicolon worked.
Finally, the example in RFC1867 DO use a comma so in the end I'm unsure what really is the correct way to do it, but semicolon solved it for me.
Related
I hope this is not a duplicate...
I am trying to POST user email & password to a php file and it seems that the php file isn't getting those values.
The js code:
function ReceiveLoginData() {
let text = this.responseText;
console.log(text);
let json_data = JSON.parse(
text.substring(1, text.length - 1).replaceAll("\\u0022", "\"")
);
// there is a lot more code... but its irrelevant.
}
function SubmitLogin() {
var email_addr = document.getElementsByClassName("login-email")[0].value;
var passwd = document.getElementsByClassName("login-passwd")[0].value;
var req = new XMLHttpRequest();
req.onload = ReceiveLoginData;
// req.onreadystatechange = ReceiveLoginData; // does not work...
req.open("POST", "/users/auth/login.php"); // ...,true); or ...,false); fail too...
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
let data_to_send = "uemail=" +
window.encodeURIComponent(email_addr) +
"&upasswd=" +
window.encodeURIComponent(passwd);
// data_to_send = "uemail="+email_addr ... works neither
req.send(data_to_send);
}
PHP (actually its location is localhost:4000/users/auth/login.php)
<?php
$uemail = $_POST["uemail"];
$upasswd = $_POST["upasswd"];
$login_err = true;
// set it to false otherwise
function SendData(string $str)
{
echo json_encode($str, JSON_HEX_QUOT | JSON_HEX_APOS);
}
function main_fn()
{
$uemail = strtolower($uemail);
if (strlen($uemail) == 0) {
SendData("[\"noemail\"]");
}
// and much more but again irrelevant...
}
main_fn();
?>
I learnt that using window.encodeURIComponent(...) is safer from here: https://stackoverflow.com/a/17382629/18243229
but neither of the ways work.
Whatever I got to know after literal 5 hours of debugging and getting fed up(I blame my noviceness):
The PHP form is being executed. ReceiveLoginData function prints ["noemail"] whenever the submit button is pressed
The Network debugging tab in chrome's dev tools shows that connection is established with php file.
Some information which might just be useful:
Response Headers (source):
HTTP/1.1 200 OK
Host: localhost:4000
Date: Sun, 18 Sep 2022 16:59:49 GMT
Connection: close
X-Powered-By: PHP/8.1.10
Content-type: text/html; charset=UTF-8
Request Headers (source):
POST /users/auth/login.php HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 31
Content-type: application/x-www-form-urlencoded
Host: localhost:4000
Origin: http://localhost:4000
Referer: http://localhost:4000/users/auth/auth.html?
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
sec-ch-ua: "Google Chrome";v="105", "Not)A;Brand";v="8", "Chromium";v="105"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Linux"
Payload: (source | URL encoded)
uemail=email%40gmail.com&upasswd=1234
uemail: email%40gmail.com
upasswd: 1234
Response:
"[\u0022noemail\u0022]"
What else I did...
I didn't waste those 5 hours on this project...
I tried to remake a smaller project with the same mechanism and the same js code calling a PHP file and voila, the php file got the values posted to it...
Everything "seems" correct according to my knowledge but why does PHP not get the $_POST values?
Also, I'm currently focusing on Google Chrome and am on Linux (ig that makes no difference...)
From the code you have posted i can spot one problem.
the $uemail = $_POST["uemail"]; is in the global scope and the code inside the main_fn function is trying to use that variable but that variable is not available in that scope because it is only available in the global scope. So it seems to me you need to pass them as arguments to get them into the functions scope.
Changeing the function definition
from: function main_fn()
to: function main_fn($uemail, $upasswd)
and calling it
with: main_fn($uemail, $upasswd);
instead of: main_fn();
should do the trick
Hope this helps :-)
In my HTML file, I have a single line (below) that gets a response from a device over WiFi & makes data available to my JavaScript, it works well except when there is an error in the response text & it halts.
<script type="text/javascript" src="http://192.168.4.1/"></script>
The response is a text string representing JavaScript variables, it sometimes gets corrupted throwing an error.
Examples:
Uncaught SyntaxError: invalid assignment left-hand side 192.168.4.1:14:3
SyntaxError: unterminated string literal 192.168.4.1:3:8
I need a solution to capture the error of parsing the received string so I can run the above statement again to get a new string.
Thanks
below is a picture of the response string (JS variables.)
The issue is that once the script is loaded, its content might be misformed and I would like to avoid having errors due to that.
Update #1. get the data with XMLHttpRequest
<script>
const Http = new XMLHttpRequest();
const url='http://192.168.4.1/';
Http.open("GET", url);
// Http.setRequestHeader('Access-Control-Allow-Origin', '*');
Http.send();
ReturnVar = Http.responseText
Http.onreadystatechange = (e) => { console.log(Http.responseText) }
</script>
Below is the detail in browser Console, Headers after script above run.
GET
scheme http
host 192.168.4.1
filename /
Address 192.168.4.1:80
Transferred 1.82 KB (1.82 KB size
Request headers (278B)
GET /
Host: 192.168.4.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: null
DNT: 1
Connection: keep-alive
Cache-Control: max-age=0
You have a script tag, which is loading the script and evaluating its content. You need to programmatically modify this, so you need to
Get the file as a text
https://www.freecodecamp.org/news/here-is-the-most-popular-ways-to-make-an-http-request-in-javascript-954ce8c95aaa/
You just need to programmatically download the content the file, not as the src of a script.
Evaluate it
Before you proceed, read this: https://javascriptweblog.wordpress.com/2010/04/19/how-evil-is-eval/
Now, that you know that eval is not exactly popular - and rightly so - and if you are still sure you need to run a script as it is, study eval and make it work equivalently as it was
Encapsulate it into try-catch
Example for error:
eval("'");
Example for error in try-catch:
try {eval("'")} catch (ex) {}
you can call the address using ajax, and then if the response is in json you can easily use it, but if its some sort of script or something else, wrap it in a script tag an append it to the body.
assume the result is what you got from ajax request and you have jQuery in your project:
$('body').append('<script>'+result+'</script>')
in this case you can wrap your code in try catch and handle the errors
I'm testing oData functinality with SAPUI5.
So far I've implemented GET & DELETE which is working fine.
No when I want to add a new Entry I get some problems.
Im sending the Create-Request like this:
var oEntry = {};
oEntry.EmailAdresse = sap.ui.getCore().getElementById("txtemail").getValue();
oEntry.Nachname = sap.ui.getCore().getElementById("txtlastname").getValue();
oModel.create("/Z_ETT_ODATASet", oEntry, function(oData, response){
alert("Success");
},
function(oError){
alert(oError);
});
This is my Request:
Accept application/json
Accept-Encoding gzip, deflate
Accept-Language en
Content-Length 54
Content-Type application/json
Cookie ZZZZZZZZ
Host XXXXXXXXXXXXXXXXXXXX
MaxDataServiceVersion 2.0
Referer http://XXXXXXXXXXXXXXXXXXXX:8007/sap/bc/ui5_ui5/sap/zhelloworld/index.html?sap-client=100&sap-ui-language=EN&sap-ui-xx-devmode=true
User-Agent Mozilla/5.0 (Windows NT 6.3; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0
X-CSRF-Token AAAAAAAAAAAAAAAA==
dataserviceversion 2.0
sap-cancel-on-close true
sap-contextid-accept header
Also the data I want to post is mentioned in the request:
{"EmailAdresse":"testemail#test.de","Nachname":"Test"}
When I add an external Breakpoint to my SAP GUI I can see that Z_ETT_ODATASET_CREATE_ENTITY is invoked, but sadly IT_KEY_TAB has no entries.
What is wrong here - where am I losing my data ?
IT_KEY_TAB is going to give you the keys that are entered in the REST service (for example the key for the entity that you were using for GET or DELETE).
For POST and PUT requests, you want the object that is being sent, which can be retrieved using the io_data_provider->read_entry_data( ) method.
I have a text input box, within a SPA built on AngularJS, for users to add a title to a printout. The input box is declared like this:
<input class="chart-title" type="text" ng-model="chartTitle" ng-change="titleChanged()"/>
The text box is filled with a default title provided by the server. A user may change the title to whatever suits them. When the title is changed, the server is updated and sends back a new title in the header of the response which then replaces the title in the box. This works perfectly for standard ASCII type characters.
However, for unicode characters (for example àßéçøö) it does not work. The text is sent down correctly, updated on the server correctly, and returned to the SPA correctly. The headers for the request/response are here:
Request URL:http://blahblahblah/api/.....&chartTitle=Instrument:%20%C3%A0%C3%9F%C3%A9%C3%A7%C3%B8%C3%B6
Response Headers:
chartTitle: Instrument: %C3%A0%C3%9F%C3%A9%C3%A7%C3%B8%C3%B6
The request is made using AngularJS $http(). As you can see the values match up (the space in the request codes out as %20 for obvious reasons). However, when I retrieve the header, using headers("charttitle"), the value I receive is Instrument: à Ãéçøö
The javascript bundle is declared in the index with charset utf-8:
<script src="/js/bundle.js" type="text/javascript" charset="UTF-8"></script>
In addition the html is declared with the correct charset, it seems to me in two places within the head declaration:
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
<meta charset="utf-8" />
According to this website (http://www.i18nqa.com/debug/utf8-debug.html) it appears that I am getting Windows1252 character encoding. This does not make any sense. I could, if absolutely necessary, write a horrible hack converting the utf-8 string to Windows1252 characters, but this seems a little extreme and quite error prone to me.
The effect is the same, whether on Chrome, Firefox or IE11. The full request headers are here:
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:en-GB,en-US;q=0.8,en;q=0.6
Connection:keep-alive
Host:blahblahblah
Origin:http://blahblahblah
Referer:http://blahblahblah/
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
Is there anything I have left out? Anything that has been forgotten?
EDIT
Full response headers as requested.
Access-Control-Allow-Origin:*
Access-Control-Expose-Headers:chartTitle
Cache-Control:private
chartTitle:Instrument: %C3%A0%C3%9F%C3%A9%C3%A7%C3%B8%C3%B6
Content-Disposition:attachment; filename=PrintData.pdf
Content-Length:1391643
Content-Type:application/octet-stream
Date:Fri, 20 Jan 2017 11:19:07 GMT
Server:Microsoft-IIS/10.0
X-AspNet-Version:4.0.30319
X-Powered-By:ASP.NET
X-SourceFiles:=?UTF-8?B?QzpcR2l0XEVPU1xSZXZpZXdlci5XZWJcYXBpXFByaW50XGQyOTNkNjA4NWVlYzlhNTEwYjQ5YThmZGQxNjNhMjAwMWZhYTFjMGY5YzhiMzUxYzE5ZjYxYWMwYTY1OWVhMDM=?=
Code around the headers
$http({
method: 'GET',
url: filePath,
params: {
fileName: fileName
},
responseType: 'arraybuffer',
headers: {'Content-Type' : 'application/json; charset=UTF-8'}
}).success(function (data, status, headers) {
ready();
if (status == 200) {
var chartTitle = headers("charttitle");
var printoutInformation = {'chartTitle' : chartTitle, 'pdfData' : data};
deferred.resolve(printoutInformation);
}
else {
deferred.resolve(null);
}
}).error(function (data) {
ready();
console.log(data);
});
return deferred.promise;
EDIT
The web.config for the api also specifies utf-8:
<globalization requestEncoding="utf-8" responseEncoding="utf-8"/>
TL;DR
In a text box I want to display "Instrument àßéçøö" and instead I am seeing "Instrument: à Ãéçøö"
Here is your issue solved.
Based on this source,
UTF-8 character debugging and its encoding and decoding
The response you are getting is the actual charecter of the encoded utf-8 string
So, you need to decode that inorder to get your result.
HEre is the code to do it.
decoded = decodeURIComponent('%C3%A0%C3%9F%C3%A9%C3%A7%C3%B8%C3%B6')
console.log(decoded);
The result is => "àßéçøö"
we have to do this to get the actual string instead of UTF-8
So, from your response you got,à Ãéçøö
decodeURIComponent(escape("à Ãéçøö")) => "àßéçøö"
DEFINITION:
decodeURIComponent():
A new string representing the decoded version of the given encoded Uniform Resource Identifier (URI) component.
So , here is your method.
if (status == 200) {
var original = headers("charttitle");
var chartTitle = decodeURIComponent(escape(original));
console.log(chartTitle);
var printoutInformation = {'chartTitle' : chartTitle, 'pdfData' : data};
deferred.resolve(printoutInformation);
}
Now, you will get the headers same as you send.
Try below for encoding
myAngApp1=document.getElementById("ItemSearch");
var uri = myAngApp1.value;
var place = encodeURIComponent(uri)
I've read tons of articles about sending image to server via java script, but I didn't get my answer and I'm still looking for response.
Some of the links:
Example of multipart/form-data
What is http multipart request?
What is the boundary in multipart/form-data?
Upload image using javascript
Most of these links have used file object to get the file, but my problem is that I don't have a file input.
I get user's image via google drive I have the link and it works as expected, I can see the image. We have a web server (python) and I have to send the picture to web service. I turned it into a binary and set boundry and multipart-formdata:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryn1mushSV09kpAN4G
Accept: */*
Referer: http://localhost:8080/app/profile
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Cookie: _ga=GA1.1.2037701396.1452595438; _gat=1;
------WebKitFormBoundaryn1mushSV09kpAN4G
Content-Disposition: form-data; name="file"; filename="photo3887649996827300720.jpg"
Content-Type: image/png
And some Binary data here.......
------WebKitFormBoundaryn1mushSV09kpAN4G
Now when I send my request to server on the other side web server cannot get it($_FILES['file'] in PHP or request.files['file'] is empty!)
Should I do something else? I think I have formed multipart-formdata correctly.
I even checked the whole response on server by nc -l IPADDRESS PORT (you see the result above)
My javascript code:
var body = '------WebKitFormBoundaryHwezUEKNYSfCHWwd'+ '\r\n' +
'Content-Disposition: form-data; name="file"; filename="test.jpg"\r\n'+
//'Content-Transfer-Encoding: base64'+"\r\n"+
'Content-Type: image/png\r\n\r\n'+
data.responseText + '\r\n' +
'------WebKitFormBoundaryHwezUEKNYSfCHWwd--\r\n';
debugger;
$.ajax({
type: "POST",
url: Config.api.path + 'storage',
data: body,
headers: {
"content-type": "multipart/form-data; boundary=" + '------WebKitFormBoundaryHwezUEKNYSfCHWwd',
//'Content-Transfer-Encoding': 'base64'
},
success: function (data) {
$scope.putUserImage(data.file_path, $scope.userIno);
}
});