Apollo REST Data Source and Imgur API - Keep getting 400 Bad Request using form data - multipartform-data

I am trying to implement apollo-datasource-rest to handle image uploading by URL via Imgur's API (documentation here: https://apidocs.imgur.com/)
I initially was getting a 400 error which read We don't support that file type!, and determined that it was due to apollo-datasource-rest automatically setting the Content-Type to application/json. After fixing that issue using the form-data npm package, here's what my code looks like:
class ImgurAPI extends RESTDataSource {
constructor() {
super();
this.baseURL = "https://api.imgur.com/3/";
}
willSendRequest(request) {
request.headers.set("Content-Type", "multipart/form-data");
request.headers.set(
"Authorization",
`Client-ID ${process.env.IMGUR_CLIENT_ID}`
);
console.log(request);
}
async uploadImageFromUrl(url) {
const formData = new FormData();
formData.append("image", url);
return this.post("upload", formData);
}
}
I now no longer get the We don't support that file type! error, but I still get a 400 response with a status text of just Bad Request. The console.log() from the previous code snippet prints this:
{
method: 'POST',
path: 'upload',
body: FormData {
_overheadLength: 104,
_valueLength: 80,
_valuesToMeasure: [],
writable: false,
readable: true,
dataSize: 0,
maxDataSize: 2097152,
pauseStreams: true,
_released: false,
_streams: [
'----------------------------594660553626244976225816\r\n' +
'Content-Disposition: form-data; name="image"\r\n' +
'\r\n',
'https://upload.wikimedia.org/wikipedia/commons/a/a0/Sunflower_as_gif_websafe.gif',
[Function: bound ]
],
_currentStream: null,
_insideLoop: false,
_pendingNext: false,
_boundary: '--------------------------594660553626244976225816'
},
params: URLSearchParams {},
headers: Headers {
[Symbol(map)]: [Object: null prototype] {
'Content-Type': [Array],
Authorization: [Array]
}
}
}
What am I missing here? The API seems to be accepting my form data so I would think that maybe there's some issue with one of the other headers, but in Postman it looks like there's only a few required headers, most of which are calculated (e.g. Content-Length) and so I assume apollo-datasource-rest must be handling that.

After doing some more research into multipart/form-data, I found that the boundary parameter is mandatory, and must be added to the Content-Type value in the request for the server to be able to parse the payload. Furthermore I am unable to manually set it as a parameter in the request (at least not in a way that actually works). Postman normally calculates this field when the request is sent, but apollo-datasource-rest doesn't automatically handle that.
Changing the Content-Type to application/x-www-form-urlencoded and using a url encoded string instead of form-data fixed the issue.
Here's the updated code:
willSendRequest(request) {
request.headers.set("Content-Type", `application/x-www-form-urlencoded`);
request.headers.set(
"Authorization",
`Client-ID ${process.env.IMGUR_CLIENT_ID}`
);
console.log(request);
}
async uploadImageFromUrl(url) {
const formData = `image=${url}&album=${process.env.IMGUR_ALBUM_DELETE_HASH}&type=url`;
return this.post("upload", formData);
}

Related

How to post data to a local route using fetch in flask/react.js?

I am trying to send the contents of a flashcard to a backend route http://127.0.0.1:5000/post onClick and it works when I send the data to webhook.site but when I change it to http://127.0.0.1:5000/post I get the error " question = data['question' TypeError: 'NoneType' object is not subscriptable ". Here is the code for the fetch request:
async function postData() {
try {
let result = await fetch('http://127.0.0.1:5000/post', {
method: 'POST',
mode: 'no-cors',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
key: `${Date.now()}`,
question: flashcard.question,
answer: flashcard.answer,
options: flashcard.options
})
});
} catch(e) {
console.log(e)
}
}
and here is the code for the backend route in flask:
#app.route('/post', methods=['GET', 'POST'])
def post():
#save data to db
data = request.get_json()
question = data['question']
answer = data['answer']
options = data['options']
key = data['key']
return jsonify({'question' : question, 'answer' : answer, 'options' : options, 'key' : key})
if __name__ == "__main__":
app.run(debug=True)
I get that the error is stating that "data" has no value which I assume means it's not recieving the JSON objects that I'm posting. It's weird because it works perfectly when I use a webhook.site url. can anyone help? thanks for your time!
Seems like your content is not a valid json request. If that is the case then content will be equal to None(not subscriptable).
Try to debug how data looks in flask and based on that you will know if its valid json.

firebase auth rest api not returning the full body response in arduino ide

i've been stuck in a problem with firebase auth rest api in arduino ide, the following code returns code 200
String url = "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=" + String(apiKey);
http.setTimeout(5000);
http.begin(url);
http.addHeader("Content-Type", "application/json");
String dataSent = "{\"email\":\"" + String(email) + "\",\"password\":\"" + String(pswd) + "\",\"returSecureToken\":\"true\"}";
int status = http.POST(dataSent);
Serial.println(status);
if (status <= 0)
{
Serial.printf("HTTP error: %s\n",
http.errorToString(status).c_str());
return false;
}
// Read the response.
String payload = http.getString();
Serial.println(payload);
but when i look in my serial monitor the response looks like this:
{
kind: "the kind of response",
localId: "someId",
email: "myEmail",
displayName: "myDisplayName",
idToken: "someIdToken",
registered: "someBoolean",
}
witch aparently is ok but when i try the same http request in postman the response includes also refreshToken and expiresIn
with even more investigation i found that localId from postman is about 980 characters while the localId from my arduino code is only about 680
im trying (and failing) to use the localId to authenticate a request with the firestore api and i think this difference in lenght is what's been buggingme.
could that really be the problem ?
method: "POST",
body: JSON.stringify({
email: enteredEmail,
password: enteredPassword,
returnSecureToken: true,
}),
headers: {
"Content-Type": "application/json",
},
})
NB: Make sure {returnSecureToken: true} is part of your request body

How to post data when you have javascript object using multipart/form-data content-type

So I have never post a data using FormData and multipart/form-data as Content-Type in my React project. But now I'm kinda forced by backend to send it this way since we have some images in payload.
The problem is that the whole data is a JS object and can be parsed to JSON, and nothing more. So how can I convert my JS object into a valid FormData that backend would accept it? Everything works without a problem in Postman, but in the app I always get the same error.
Here is some more detail:
The working Postman sample:
What I expect to be working (but obviously doesn't):
const createProd = () =>
HttpRequest.post('/api/prod', {
body: {
Product: {
title: 'Test Prod',
shop: null,
description: "My new ad's description",
category: { id: '5d8c6aa6fadeaf26b0194667' },
condition: 'USED'
}
});
HttpRequest is a helper function which uses ES6 fetch for requests.
I always get the same error: "Required request part 'Product' is not present" with/without JSON.stringify.
I even tried to create a sample FormData to at least change the error:
cont payload = new FormData();
payload.append('Product', {foo: 'bar'});
But still same error. I also copied the code which is generated by Postman. But still no chance.
I would be thankful if you share your suggestions or workarounds.
Thanks in advance.
const formData = new FormData();
const product = { //your product object };
formData.append('product', JSON.stringify(product));
const config = {
headers: {
'Content-Type': 'multipart/form-data; charset=utf-8; boundary="another cool boundary";'
}
};
axios.post(`/api/ads`, formData, config).then((res) => {
console.log(res);
}).catch(err => {
console.log(err);
});
Maybe you should set header.
Try this one. In my case I used Axios. It worked for me in my project.

How do I build a complete authorization header for axios request?

I have been searching for a while and cannot find and complete example on how to correctly build an authorization header, finding only articles that have a lot of time so I can't get it to work.
The problem is not really code, but rather "instructions", because i keep getting error "401 Unauthorized".
I have tested all the calls I need in the Postman software but i cant get in to work in a react ".jsx" file through an axios.get() method.
And I think I have one or all of these problems:
I'm missing paramaters in my header which are causing my request to be denied;
I'm inserting the keys(Client Id,Client Secret,Access token) has the wrong parameters;
I'm not doing the request correctly(?)
I'm pretty new at React, so i don't really handle well a lot of... lets say "good pratics" of coding.
Anyways, I have written the following header:
this.apiCallParams = {
method : "GET",
headers: {
"Cache-Control" : "no-cache",
"Accept" : "application/json",
"Content-Type" : "application/json"
},
body : JSON.stringify({
"client_id" : "***********",
"client_secret" : "******************************************************",
"access_token" : "**************************************--",
"grant_type":"client_credentials"
})
}
And made the request like this:
this.state ={
requestURL : "https://api.predicthq.com/v1/events?q=" + this.props.location.state.textInput,
searchResultsList : [],
}
}
componentDidMount(){
axios.get(this.state.requestURL, this.apiCallParams).then( res => { const searchResultsList = res.data; this.setState({ searchResultsList : searchResultsList.results })});
}
I have replaced the keys as the same amount of "*", don't know if that matters,and left the "--" because it actually belongs to the access token, either way, I don't know why I keeping getting 401, can some one tell me what is wrong with my header.

Multipart form parse error - Invalid boundary in multipart: None

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 "-----".

Resources