I tried to use Walmart API v4.2 to publish some items. I used "Bulk Item Setup" API method to create some feed. I used some types of ways to did it:
Send binary file (in the request body, for-data) with header "multipart/form-data" (this way was described in the Walmart API docs)
Send stringified object in the request body with header 'Content-Type': 'application/json',
Walmart API correctly returns me feedId.
But all of these ways didn't work! After feed creating, I saw "Submitted" status at the Walmart Seller Center. But this status was changed after few minutes to "Error". At the error column I see "ERROR TYPE: Data Error" with a description "Malformed data. Please check data file to ensure it is formatted properly.java.lang.NullPointerException".
I use my backend NodeJs app to do it. I use Axios for making a request.
My code example:
async createFeed(wdpId, wdpSecret, accessToken, feedsData) {
try {
const string = JSON.stringify(feedsData);
const file = Buffer.from(string);
const formData = new FormData();
formData.append('file', file);
const baseToken = WalmartService.getBaseAuthToken(wdpId, wdpSecret);
const options = {
params: {
feedType: 'MP_WFS_ITEM',
},
headers: {
Authorization: baseToken,
'WM_SEC.ACCESS_TOKEN': accessToken,
'WM_QOS.CORRELATION_ID': uuidv4(),
'WM_SVC.NAME': 'Walmart Marketplace',
Accept: 'application/json',
'Content-Type': 'application/json',
...formData.getHeaders(),
},
};
return (
axios
.post(`${process.env.WALMART_API_BASEURL}/feeds`, formData, options)
.then((response) => {
return response.data;
})
.catch((error) => {
console.error(error.message);
throw new BadRequestException('Walmart error, ', error.message);
})
);
} catch (error) {
throw new BadRequestException('Can not create listing');
}
}
It is difficult to identify the exact issue based on the information you provided. Few things that you might want to check
If you are appending/attaching a file (as I see it in the code), use Content-Type header as "multipart/form-data. Also, make sure the file name has a .json extension if you are sending data as a json string. If you don't use this, it might default to xml and you will get the same error as what you see.
Try invoking the API using a rest client like Postman and verify if that call is successful.
If you do want to send the data as HTTP body (instead of a file), that should work too with Content-Type as application/json. This has not been documented on their developer portal, but it works.
Related
This is what my "dev" sent me. Someone help please
I'm trying my best, but their API doesn't respond to our methods. This authentication is the root of the problem. I'm right now using Axios(the most popular and only method for making API requests for web apps) but it's not accepting request
and then i told him i would ask for help*
You can ask this question- ` How do I make requests for creating order API in my express app? I've tried to make the request by getting my form data from my EJS form using the request.body. But still, it is saying error 400.
Here is his code:
app.post('/order-labels', checkAuthenticated, (req, res) => {
const data = JSON.stringify(req.body);
console.log(data)
const config = {
method: 'post',
url: 'https://labelsupply.io/api/order',
headers: {
'X-Api-Auth': '32854090-03dd-a3c1-Deleted some for safety',
'Content-Type': 'application/x-www-form-urlencoded'
},
data: data
};
axios(config)
.then(function(response) {
console.log(response.data);
})
.catch(function(error) {
console.log(error);
});
})
by console.logging we are getting the data, but the API doesn't accepting
The API Docs are here.
you may need an account to view just put junk
The API calls for url encoded string.
const data = JSON.stringify(req.body);
console.log(data)
data = new URLSearchParams(Object.entries(data)).toString();
console.log(data); // now should be URL encoded
const config = {
method: 'post',
url: 'https://labelsupply.io/api/order',
headers: {
'X-Api-Auth': '32854090-03dd-a3c1-Deleted some for safety',
'Content-Type': 'application/x-www-form-urlencoded'
},
data: data
};
See if the API likes the new encoding?
I have a React application where I am changing POST method to GET with the request body as it is. It works fine with POST request however when I change the method to GET, it gives me error-
message: "org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public
My Front End Code-
export const setData = (getData) => dispatch => {
axios({
method: 'GET',
url: 'http://localhost:8080/api',
headers: {
'Content-Type': 'application/json'
},
data: getData
})
.then (response => {
dispatch({
type: API_DATA,
payload: response.data
})
dispatch({
type: SET_SEARCH_LOADER,
payload: false
})
})
.catch(function(error) {
})
}
Can someone let me know what I am missing here. As per my understanding, http allows to have a request body for GET method.
As per my understanding, http allows to have a request body for GET method.
While this is technically true (although it may be more accurate to say that it just doesn't explicitly disallow it), it's a very odd thing to do, and most systems do not expect GET requests to have bodies.
Consequently, plenty of libraries will not handle this.
The documentation for Axois says:
// `data` is the data to be sent as the request body
// Only applicable for request methods 'PUT', 'POST', and 'PATCH'
Under the hood, if you run Axios client side in a web browser, it will use XMLHttpRequest. If you look at the specification for that it says:
client . send([body = null])
Initiates the request. The body argument provides the request body, if any, and is ignored if the request method is GET or HEAD.
If you want to send parameters with get request in axios, you should send parameters as params.
If you want to set "Content-type":"application/json" and send params with get request, you should also send an empty data object.
For example:
const AUTH_TOKEN = 'Bearer token'
const config = {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': AUTH_TOKEN,
},
data: {},
params: {
"post_id": 1
}
}
axios.get("http://localhost/api/v1/posts/", config)
This is not axios, the error origniates from the java backend you're talking to. The public field in your request body is missing.
If you just want to send the data as parameters (which would be odd), pass it using params instead of data (as shown here: https://github.com/axios/axios#example).
I personally don't think your API should support GET with a request body (talk to the devs and ask for documentation).
I'm trying to send an image and some data from an API to another. The image is stored in memory with multer. But when I want to send it, the body is just empty. I tried the same request with postman and it worked perfectly.
postman test
postman test image
server test
server test image
Here is some code. I removed some of it so you can read it better
export const saveImage = async ({ image, name, folder, options }: { image: any, name?: any, folder: string, options?: any }) => {
try {
const fd = new FormData();
fd.append("image", image.buffer, image.originalname);
if(options) {
fd.append("options[resize][height]", options?.resize?.height);
fd.append("options[resize][width]", options?.resize?.width);
}
if(name) fd.append("name", name);
fd.append("folder", folder);
fd.append("servideId", IMAGES_ID);
fd.append("serviceSecret", IMAGES_SECRET);
console.log(fd)
const formHeaders = fd.getHeaders();
const request = await axios.post(`${IMAGES_URL}/api/images`, {
headers: formHeaders,
body: fd
});
return request.data.id;
} catch (error) {
const { response } = error;
console.log(response.request.data)
if(error?.response?.data?.error) {
throw { statusCode: error.response.status, message: error.response.data.error }
}
console.error("Images API", error);
throw new InternalError("Something gone wrong");
}
}
When I log the FormData, I can see in _streams, the data that I'm sending, but the Images API receives an empty body.
FormData screenshot
If you need more information tell me, please! Thank you
The axios API for the post method is: axios.post(url[, data[, config]]). The second argument must always be the data you send along.
In your case axios thinks { headers: formHeaders, body: fd } is the body and the request ends up being application/json. To send a file with data using axios in Node.js, do the following:
const response = await axios.post(`${IMAGES_URL}/api/images`, fd, {
headers: {
...formHeaders,
'X-Custom-Header': 'lala', // optional
},
});
Your question inspired me to turn this answer into an article — Send a File With Axios in Node.js. It covers a few common pitfalls and you'll learn how to send files that are stored as a Buffer or coming from a Stream.
With Axios, you can directly use the form data without having to deal with headers.
axios.post("/api/images", fd)
If you wish to modify headers at some point in the future, you should pass the formData to the `data` field instead of `body`.
axios.post("/api/images", { headers: formHeaders, data: fd })
Correction in comments.
It can also be done using the Axios API syntax.
axios({method: 'post', url: 'url', data: fd, headers: {} })
In the backend, multer will add your files to req.file instead of req.body, if you have properly configured it to do so.
When I call the route https://api.linkedin.com/v2/assets?action=registerUpload with axios
I have a response like this
{
"value": {
"uploadMechanism": {
"com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest": {
"uploadUrl": "https://api.linkedin.com/mediaUpload/C4E22AQGSNx_ko_tzLw/feedshare-uploadedImage/0?ca=vector_feedshare&cn=uploads&m=AQK-VkaHKm7-NQAAAXMad6RKlbsQGW9Vi3JI0iFnlEKhTdhVcSZxyxUJ5g&app=17412253&sync=1&v=beta&ut=0MQzOzxqQ7m9k1",
"headers": {
"media-type-family": "STILLIMAGE"
}
}
},
"asset": "urn:li:digitalmediaAsset:C4E22QSGSNx_ko_tzLw",
"mediaArtifact": "urn:li:digitalmediaMediaArtifact:(urn:li:digitalmediaAsset:C4E22AQGSNx_ko_tzLw,urn:li:digitalmediaMediaArtifactClass:feedshare-uploadedImage)"
}
}
I want to upload an image from my server with the upload link
var newFile = fs.createReadStream(__dirname+"/temp/lion.png");
const form_data = new FormData();
form_data.append('file', newFile);
const request_config = {
headers: {
'Authorization': `Bearer ${access_token}`,
"Content-Type": "multipart/form-data"
},
data: {content : form_data['_streams']['1']}
};
const res = await axios.put('https://api.linkedin.com/mediaUpload/C4E2OIQNQE5ILcQCU_lLA/feedshare-uploadedImage/0?ca=vector_feedshare&cn=uploads&m=AQIy5jpkZ0ut2AAAAXMadpS8A97cK9wOSjzagaNHo97bRPCYVZt7f5E4yQ&app=17411153&sync=1&v=beta&ut=2JZ18aO4E6m9k1', form_data, request_config);
But I have this response from the server
"Error: Request failed with status code 400\n at createError (/home/node/app/node_modules/axios/lib/core/createError.js:16:15)\n at settle (/home/node/app/node_modules/axios/lib/core/settle.js:17:12)\n at IncomingMessage.handleStreamEnd (/home/node/app/node_modules/axios/lib/adapters/http.js:236:11)\n at IncomingMessage.emit (events.js:198:15)\n at endReadableNT (_stream_readable.js:1139:12)\n at processTicksAndRejections (internal/process/task_queues.js:81:17)"
7 Months old question, but still an issue today, was trying to figure this thing for few hours.
Could have been much easier if only Linkedin would add (like a normal API) the information they expect to get, like the Content-Type header, or what encoding.
I have a URL I need to upload as an image, so I have do some extra steps, but the general idea is you need to include the Content-Type of your image, use the URL they give you to send the file, and get the file as ArrayBuffer, as well as not forget to use the PUT method.
to get ArrayBuffer, and get the content type of the file:
axios.get(imageUrl, {responseType: 'arraybuffer'}).then((imageData) => {
const contentType = imageData.headers['content-type'];
//The ArrayBuffer is at imageData.data
})
NOTE- Don't forget to include supportedUploadMechanism: ['SYNCHRONOUS_UPLOAD'], so the it will upload synchronously, and you won't have to send another request to make sure the upload was done successfully.
const registerUploadRequest = {
owner: this.owner,
recipes: ['urn:li:digitalmediaRecipe:feedshare-image'],
serviceRelationships: [
{
identifier: 'urn:li:userGeneratedContent',
relationshipType: 'OWNER',
},
],
supportedUploadMechanism: ['SYNCHRONOUS_UPLOAD'],
};
After I got the image data I need, I send request to /assets?action=registerUpload so I can get the id of the asset, as well as the URL I need to upload it to:
return this.post('/assets?action=registerUpload', {registerUploadRequest}).then((result) => {
const url = result.value.uploadMechanism['com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest'].uploadUrl,
assetId = result.value.asset.replace('urn:li:digitalmediaAsset:', '');
})
Note - we need the id to use the asset after upload.
After I finally got all the data I need, I simply PUT the ArrayBuffer to the url given, with the correct content-type.
return axios.put(url, imageData.data, {
headers: {
'Authorization': `Bearer ${this.accessToken}`,
'Content-Type': contentType,
},
})
And you are done.
IMPORTANT - they do not send any response in the data object, if the request was successful the image uploaded, if it failed, it didn't.
I'm switching one of my projects from request over to something a bit more light-weight (such as got, axios, or fetch). Everything is going smoothly, however, I'm having an issue when attempting to upload a file stream (PUT and POST). It works fine with the request package, but any of the other three return a 500 from the server.
I know that a 500 generally means an issue on the server's end, but it is consistent only with the HTTP packages that I'm testing out. When I revert my code to use request, it works fine.
Here is my current Request code:
Request.put(`http://endpoint.com`, {
headers: {
Authorization: `Bearer ${account.token.access_token}`
},
formData: {
content: fs.createReadStream(localPath)
}
}, (err, response, body) => {
if (err) {
return callback(err);
}
return callback(null, body);
});
And here is one of the attempts using another package (in this case, got):
got.put(`http://endpoint.com`, {
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${account.token.access_token}`,
},
body: {
content: fs.createReadStream(localPath)
}
})
.then(response => {
return callback(null, response.body);
})
.catch(err => {
return callback(err);
});
Per the got documentation, I've also tried using the form-data package in conjunction with it according to its example and I still get the same issue.
The only difference between these 2 I can gather is with got I do have to manually specify the Content-Type header otherwise the endpoint does give me a proper error on that. Otherwise, I'm not sure how the 2 packages are constructing the body with the stream, but as I said, fetch and axios are also producing the exact same error as got.
If you want any of the snippets using fetch or axios I'd be happy to post them as well.
I know this question was asked a while ago, but I too am missing the simple pipe support from the request package
const request = require('request');
request
.get("https://res.cloudinary.com/demo/image/upload/sample.jpg")
.pipe(request.post("http://127.0.0.1:8000/api/upload/stream"))
// Or any readable stream
fs.createReadStream('/Users/file/path/localFile.jpeg')
.pipe(request.post("http://127.0.0.1:8000/api/upload/stream"))
and had to do some experimenting to find similar features from current libraries.
Unfortunately, I haven't worked with "got" but I hope the following 2 examples help someone else that are interested in working with the Native http/https libraries or the popular axios library
HTTP/HTTPS
Supports piping!
const http = require('http');
const https = require('https');
console.log("[i] Test pass-through: http/https");
// Note: http/https must match URL protocol
https.get(
"https://res.cloudinary.com/demo/image/upload/sample.jpg",
(imageStream) => {
console.log(" [i] Received stream");
imageStream.pipe(
http.request("http://localhost:8000/api/upload/stream/", {
method: "POST",
headers: {
"Content-Type": imageStream.headers["content-type"],
},
})
);
}
);
// Or any readable stream
fs.createReadStream('/Users/file/path/localFile.jpeg')
.pipe(
http.request("http://localhost:8000/api/upload/stream/", {
method: "POST",
headers: {
"Content-Type": imageStream.headers["content-type"],
},
})
)
Axios
Note the usage of imageStream.data and that it's being attached to data in the Axios config.
const axios = require('axios');
(async function selfInvokingFunction() {
console.log("[i] Test pass-through: axios");
const imageStream = await axios.get(
"https://res.cloudinary.com/demo/image/upload/sample.jpg",
{
responseType: "stream", // Important to ensure axios provides stream
}
);
console.log(" [i] Received stream");
const upload = await axios({
method: "post",
url: "http://127.0.0.1:8000/api/upload/stream/",
data: imageStream.data,
headers: {
"Content-Type": imageStream.headers["content-type"],
},
});
console.log("Upload response", upload.data);
})();
Looks like this was a headers issue. If I use the headers directly from FormData (i.e., headers: form.getHeaders()) and just add in my additional headers afterwards (Authorization), then this ends up working just fine.
For me just works when I added other parameters on FormData.
before
const form = new FormData();
form.append('file', fileStream);
after
const form = new FormData();
form.append('file', fileStream, 'my-whatever-file-name.mp4');
So that way I can send stream from my backend to another backend in node, waiting a file in multipart/form-data called 'file'