I need to upload a file together with some meta info via multipart/form-data like this:
Header
----boundary
Meta
----boundary
File
----boundary
For some reason I always get a 400 Error, the Backend server tells me, that the Meta-Field is missing. However, if I look into the Hex/Ascii dump with Wireshark, the field is definitely there.
The following Curl-command works perfectly fine and the file gets uploaded successfully:
curl -H "Expect:" -v -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: "token" -F "File=#/location" -F 'Meta={"access":"token"}' http://path
Therefore this doesn't seem to be a Backend failure. My Angular (4.1.3) request must be bad, but I can't figure out what's wrong with it.
Template-Code:
<input #file_input type="file" (change)="onFileInputChange($event)" multiple>
Angular2-Code:
onFileInputChange(event) {
let fileList: FileList = event.target.files;
for (let i = 0; i < fileList.length; i++) {
let fp: File = fileList[i];
let formData: FormData = new FormData();
formData.append('File', fp, fp.name);
const body = JSON.stringify({
'access': "token"
});
this.formData.append('Meta', body);
let RequestHeader = new Headers();
// auto set content type
RequestHeader.append('Content-Type', '');
RequestHeader.append('Accept', 'application/json');
RequestHeader.append('Authorization', "token");
this.http.post(this.backend_url, formData, RequestHeader).map(
res => res.json()).subscribe(
data => console.log('success'),
error => console.log(error))
}
}
What am I missing here?
I finally found the solution.
The Content-Type header was malformed:
Content-Type: , multipart/form-data; boundary=----WebKit12345
The content type header in the actual code was preset.
By calling
headers.set('Content-Type', '');
I assumed, that the header content type was overwritten, but actually an empty string was prepended to it, so that there was a comma, which could not be parsed, of course.
I solved this by deleting the content type header completely:
headers.delete('Content-Type');
Related
I'm trying to send image file and data in one post request with axios:
const fd = new FormData();
fd.append("image", selectedFile, selectedFile.name);
let data = {
category: category,
title: title,
post: text,
image: fd
};
//console.log(fd);
const headers = {
"Content-Type": "multipart/form-data"
};
const url = "http://localhost:5000/blog/posts";
axios
.post(url, fd, headers)
.then(resp => { ...
Flask back-end:
image = request.files['image']
category = request.files['category']
title = request.files['title']
post = request.files['post']
log output:
werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.
KeyError: 'category'
What am I doing wrong ?
You use a FormData object which is transmitted as "multipart/formdata". In other words, in multiple parts of data that are transmitted in individual sections of a broadcast.
Use the FormData object for all data that you want to transfer. Add the data as entries to the FormData object.
const fd = new FormData();
fd.append ("image", selectedFile, selectedFile.name);
fd.append("category", category);
// ...
The different types of data and files can be reached within the route in a different way from the request object.
Use request.form in your route for all entries that do not represent a file.
image = request.files['image']
category = request.form['category']
# ...
Possibly use .get(key[, ...]) to bypass the KeyError and get None if the data is not available or to pass a default value.
category = request.form.get('category', 'Uncategorized')
You create a data object, but don't seem to do anything with it. If you're using FormData, you should append all your data points to it. So your code should look something like this:
const fd = new FormData();
fd.append("image", selectedFile, selectedFile.name);
fd.append("category", category);
fd.append("title", title);
...
Then you can just post it the way you post it:
axios
.post(url, fd, headers)
.then(resp => { ...
I'm writing a basic search function to query a postgresql db. When I'm running it and with console.logs up, I keep seeing that it's sending and returning a 200, however the object it is sending is blank. Below is my function followed by my curl command I'm sending. When I run the search function during the DB seed, it's able to find what I'm looking for.
curl http://localhost:5000/api/company/parts/GCS
companyRouter.get("/parts/:id", async (req, res, next) => {
const partNumber = req.body;
console.log("request 19", partNumber, req.body);
try {
const part = await searchPartsNumber(partNumber);
res.send({ part });
} catch ({ name, message }) {
next({ name, message });
}
});
SOLUTION -1:
you are not passing any body to the request . you are passing a url parameter. that's why your body is an empty object. if you want to get the parameter ( in your case GCS ) in your server you should do something like this
const partNumber = req.param('id');
SOLUTION - 2
otherwise if you want to pass body using curl you should modify your request like this.
curl --header "Content-Type: application/json" \
--request POST \
--data '{"partNumber":"YOUR_PART_NUMBER"}' \
http://localhost:5000/api/company/parts/GCS
and as req.body is an object. you should extract your partNumber like this
const {partNumber} = req.body;
I am trying to post data to a php file that is two file levels higher relative to the .js file. I am new to reactjs and not entirely familiar with axios so bear with me.
ReactJS code
onSubmit=(e)=>{
e.preventDefault();
alert(this.state.username);
axios.post('../../connections/submit.php',{
username:this.state.username
}).then(res=>{
console.log(res);
}).catch(error=>{
console.log(error);
});
};
The PHP file in question:
if(isset($_POST) && !empty($_POST)){
$data = json_decode(file_get_contents("php://input"), true);
$username = $data['username'];
print_r($username);
$conn=mysqli_connect("localhost","root","","jwt_test");
$sqlOrder="INSERT INTO user(u_id,username) VALUES(NULL,'$username')";
$conn->query($sqlOrder);
$conn->close;
};
Is this the correct way of posting the data? I am returned a 404 error code stating that it could not find my file.
My file structure is as so:
-connections
-submit.php
-src
-components
-submit.js
If it helps, I imported axios in my submit.js file instead of in the App.js file.
Any advice would be much appreciated. Thanks for reading.
I've got it to work. For the reactjs file, this is the code:
onSubmit=(e)=>{
e.preventDefault();
alert(this.state.username);
//!!! need to stringify the payload before sending it to phpmyadmin.
var payload = {
username:this.state.username
};
axios.post('http://localhost/connections/submit.php',
JSON.stringify(payload)).then(res=>{
console.log(res);
}).catch(error=>{
console.log(error);
});
};
The receiving PHP file:
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST");
if(isset($_POST) && !empty($_POST)){
$data = json_decode(file_get_contents("php://input"), true);
$username = $data['username'];
$conn=mysqli_connect("localhost","root","","jwt_test");
$stmt = $conn->prepare("INSERT INTO user(u_id,username) VALUES(NULL,?)");
$stmt->bind_param('s',$username);
$stmt->execute();
$conn->close;
};
What was needed were the two headers in the PHP file, encoding the payload in JSON , and using another server to receive the payload, in this case, XAMPP was used.
Goal
With nock, I am seeking a solution in mocking an a tiny PNG file upload via POST multipart/form-data.
curl: Box API upload PNG file
Following curl script presents how to upload a file through Box API, file name: 'dummy.png' in root directory '0'.
curl 'https://upload.box.com/api/2.0/files/content' \
--request POST \
--verbose \
--silent \
--header 'authorization: Bearer [** Access Token **]' \
--header 'Content-Type: multipart/form-data' \
--form attributes='{ "name": "dummy.png", "parent": { "id": "0" } }' \
--form file=#'./files/dummy.png'
Condensed response:
Success [HTTP status: 201]
{
"total_count": 1,
"entries": [
{
"type": "file",
"name": "dummy.png",
"id": "584886508967"
}
]
}
nock attempt: Box API upload PNG file
The next code snippet is using npm nock works, however, this mocking is incomplete:
const accessToken = v4();
const randomFileId = v4();
let boundary = '';
const scope = nock('https://upload.box.com/api/2.0/')
.log((m, d) => logger.debug(m, d))
.matchHeader('authorization', `Bearer ${accessToken}`);
scope
.matchHeader('content-type', val => {
const matches = val.match(/^multipart\/form-data; boundary=([a-zA-Z0-9\-]+)$/);
if (matches && matches.length > 1) {
boundary = matches[1];
}
return !!matches;
})
.post('/files/content', body => {
return true;
})
.reply(201, {
entries: [
{
id: randomFileId,
name: 'dummy.png',
type: 'file'
}
]
});
nock attempt: Missing form attributes and file binary
It is not clear to me how to include with nock code what is included in curl POST request:
--header 'Content-Type: multipart/form-data' \
--form attributes='{ "name": "dummy.png", "parent": { "id": "0" } }' \
--form file=#'./files/dummy.png'
I would like to include in nock POST request:
File dummy.png binary as defined in --form file=#'./files/dummy.png'
File upload metadata as defined by --form attributes='{ "name": "dummy.png", "parent": { "id": "0" } }'
Thank you, appreciate the assistance.
As you alluded to in your question, Nock does not require the form data in order to intercept the request and mock the response. However, if you're testing that your code is sending the correct request body, doing the assertion is good practice.
The --form flag in cURL is a helper that does different thing for different protocols.
For HTTP protocol family, this lets curl emulate a filled-in form in which a user has pressed the submit button. This causes curl to POST data using the Content-Type multipart/form-data according to RFC 2388.
Not overly helpful, but the gist is that the data you're looking for in Nock will be in the body of the POST request. The Nock code in your question is on the right track. The post method using a callback as the second argument is how you can dig into the raw data being intercepted by Nock. The not-so-straight-forward part is that the body argument passed to the callback is hex encoded because the body includes the binary data of the png file.
.post('/files/content', body => {
const decoded = Buffer.from(body, 'hex');
console.log(decoded);
return true;
})
Adding the snippet above to your existing post method should output something similar to:
----------------------------493956036364114509087826
Content-Disposition: form-data; name="attributes"
{"name":"dummy.png","parent":{"id":"0"}}
----------------------------493956036364114509087826
Content-Disposition: form-data; name="content"; filename="unused"
Content-Type: image/png
�PNG
IHDRĉ
IDATx�c��������IEND�B`�
----------------------------493956036364114509087826--
It would be up to you at that point to determine if the body includes the data you expect and return true if so.
Referencing RFC 2388 for what multipart form-data should look like could help.
I am very frustrated and could not find the soloution:
I am creating a project using angularjs and nodejs.I get image data from angular side in node js and send this data to further api.I got error
{
"error": {
"detail": "Multipart form parse error - Invalid boundary in multipart: None"
}
}
here is my code in nodejs side:
var request = require('request');
console.log(req.files);
var data = {
website:'www.gamail.xom',
contact_number:'dsdsd',
services_offered:'dsadasd',
contact_name:'dasdas',
provider_category:'exchange',
email:'kk#gmail.com',
image:req.files
}
var api_url = global.common.base_url + 'vcard/1.0.0/visit_card/' + req.param('uuid') +'/';
request({
url: api_url,
method: 'PUT',
headers: {
'Content-Type': 'multipart/form-data',
'Authorization': 'Bearer '+req.cookies.apitoken
},
json: data
}, function(error, response, body) {
if(response.statusCode == 200 && !error){
res.end(JSON.stringify(body));
}else{
res.send(response.statusCode, { error: body });
}
});
}
In req.files i get
{ image:
[ { fieldName: 'image[0]',
originalFilename: 'icon_dd_chart_grey.png',
path: 'C:\\Users\\karakuma\\AppData\\Local\\Temp\\op74_gLSzPs-_49aT1GF0si
7.png',
headers: [Object],
size: 1474,
name: 'icon_dd_chart_grey.png',
type: 'image/png' } ] }
Try defining the content type as follows.
content_type='multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW'
I was facing the same issue and it worked for me in python.
I also faced this issue while trying to upload file. For me the issue was the FormData, which was coming as Object instead of FormData instance
So i converted my object to FormData using below code:
const getFormData = object => Object.keys(object).reduce((formData, key) => {
formData.append(key, object[key]);
return formData;
}, new FormData());
And the just executed my post request, in my case using Vue resource:
return Vue.http.post(url,
getFormData(formData),
{
headers: {
'Content-Type': 'multipart/form-data'
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
There is no need to mention the content type in header, fetch() will detect it's content type by itself.
let formData = new FormData()
formData.append("email", email);
formData.append("password", password);
formData.append("image", image);
const response = await fetch('http://localhost:8000/api/authentication/register/', {
method: 'POST',
headers: {'X-CSRFToken': csrftoken}, //django specific
body: formData,
});
I have been facing this problem in angular 11 connected to Django rest API, I was able to curl with something like this in order to upload a JSON with a form:
curl -X POST -S \
-H 'Accept: application/json' \
-u "user:password" \
-F "name=name" \
-F "label=mylabel" \
-F "otherfields=something" \
-F "photo=#./example.png;type=image/png" \
http://127.0.0.1:8000/api/v1/item/
But I was getting that exception using my header as httpOptions:
'content-type': 'multipart/form-data',
So I just commented out the content-type and it seems angular is so clever that he creates the header for you and will set the multipart together with the boundaries.
For more information on this:
What is the boundary in multipart/form-data?
A boundary is just the 'key' to separate the multiple "parts" of a multipart payload. Normally something like '&' is enough to separate the variables but you need something more unique to separate the payloads within the payload comment
You can use any value that not occurs in the data sent.
NOTE: Because boundary delimiters must not appear in the body parts being encapsulated, a user agent must exercise care to choose a unique boundary parameter value.
The simplest boundary delimiter line possible is something like "---", with a closing boundary delimiter line of "-----".