How would I resolve this 422 error during POST? - javascript

I'm trying to follow a code along on Udemy except making my version of the project. After using bcrypt and jwt, I'm unable to make any posts to my app. I'm able to login fine, but when trying to post something, I get a 422 error.
This is what's getting me stuck.
const twottSubmitHandler = async (event) => {
event.preventDefault();
try {
const formData = new FormData();
formData.append("title", formState.inputs.title.value);
formData.append("description", formState.inputs.description.value);
formData.append("creator", auth.userId);
await sendRequest("http://localhost:3001/api/twotts", "POST", formData, {
Authorization: "Bearer " + auth.token,
});
history.push("/");
} catch (err) {}
};
In the twotts-controller, this is what's throwing the error,
const errors = validationResult(req);
if (!errors.isEmpty()) {
return next(new HttpError("Invalid input passed", 422));
}
const { title, description, creator } = req.body;
const createdTwott = new Twott({
title,
description,
creator,
});
Is there a way to make it so it adds it using JSON.stringify instead of using FormData? Using
try {
await sendRequest(
"http://localhost:3001/api/twotts",
"POST",
JSON.stringify({
title: formState.inputs.title.value,
description: formState.inputs.description.value,
creator: auth.userId,
}),
{ Authorization: "Bearer " + auth.token },
{ "Content-Type": "application/json" }
);
history.push("/");
} catch (err) {}
I still get invalid inputs. If I put the Authorization argument anywhere else, I'd get authentication issues.
the link to the repo is https://github.com/preintercede/Twotter (with commits) in case there's a part that I missed.

You're constructing FormData and send it (implicitly) with Content-Type: multipart/form-data. express-validator doesn't validate such data out-of-the box (reasoning: https://github.com/express-validator/express-validator/issues/276)
One solution would be to use https://github.com/expressjs/multer, but my recommendation is just to submit JSON data with correct Content-Type, as already you're doing for other requests:
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
modified: src/twotts/pages/NewTwott.js
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
# src/twotts/pages/NewTwott.js:39 # const NewTwott = () => {
event.preventDefault();
try {
- const formData = new FormData();
- formData.append("title", formState.inputs.title.value);
- formData.append("description", formState.inputs.description.value);
- formData.append("creator", auth.userId);
- await sendRequest("http://localhost:3001/api/twotts", "POST", formData, {
+ await sendRequest("http://localhost:3001/api/twotts", "POST", JSON.stringify({
+ title: formState.inputs.title.value,
+ description: formState.inputs.description.value,
+ creator: auth.userId
+ }), {
Authorization: "Bearer " + auth.token,
+ "Content-Type": "application/json",
});
history.push("/");
} catch (err) {}
I recommend to wrap this into a helper function. This could also set the Bearer token to the request headers if present.

Related

Trustpilot Authentication Error Unknown grant_type

I want to use Trustpilot API to send email review invitation. Before making that call, I need to get an access token. I'm following Trustpilot's documentation in the function below. I keep getting this error of Unknown grant_type. According to the documentation, it is supposed to be set to "password" to get the token and it is not working. I tried this solution but it is not working for me. I can't seem to know what's causing the error especially that it is very general.
trustPilot.getAuthToken = async () => {
let apiKey = process.env.TRUSTPILOT_API
let secrect = process.env.TRUSTPILOT_SECRET
let baseEncoded = Buffer.from(`${apiKey}:${secrect}`).toString('base64')
console.log(baseEncoded, 'base')
let authToken = null
try {
authToken = await axios({
method: 'POST',
url: `https://api.trustpilot.com/v1/oauth/oauth-business-users-for-applications/accesstoken`,
headers: { Authorization: 'Basic ' + baseEncoded, 'Content-Type': 'application/x-www-form-urlencoded' },
content: `grant_type=password&username=${process.env.TRUSTPILOT_EMAIL}&password=${process.env.TRUSTPILOT_PASSWORD}`,
})
console.log(authToken, 'auth')
} catch (error) {
console.log(error.response, 'err')
throw { code: '404' }
}
return authToken
}
Please take a look at axios documentation. You are passing content: instead of data:. Axios call should be like this:
authToken = await axios({
method: 'POST',
url: `https://api.trustpilot.com/v1/oauth/oauth-business-users-for-applications/accesstoken`,
headers: { Authorization: 'Basic ' + baseEncoded, 'Content-Type': 'application/x-www-form-urlencoded' },
data: `grant_type=password&username=${process.env.TRUSTPILOT_EMAIL}&password=${process.env.TRUSTPILOT_PASSWORD}`,
})

Authentication credentials are not provided - Axios

here is my action of redux:
export const addToWishlist = (id) => async (dispatch, getState) => {
try {
const {
userLogin: { userInfo },
} = getState()
const config = {
headers: {
'Authorization': `JWT ${userInfo.token}`
}
}
const { data } = await axios.post(`/api/wishlist/add_to_wishlist/${id}/`, config
)
dispatch({
type: WISHLIST_ADD_ITEM,
payload: data
})
localStorage.setItem('wishlistItems', JSON.stringify(getState().wishlist.wishlistItemsFromStorage))
} catch (error) {
dispatch({
type: WISHLIST_ADD_ITEM_FAIL,
payload: error.response && error.response.data.detail
? error.response.data.detail
: error.message,
})
}
}
so i tried to send a post request to this api end point /api/wishlist/add_to_wishlist/${id}/ it says in response(from redux extension)
type:"WISHLIST_ADD_ITEM_FAIL"
payload:"Authentication credentials were not provided."
Authentication credentials we…provided.
but when I tried the same end point using postman it worked i.e. it add the item to wishlist.
What I tried
I tried to copy the token from console and paste it on postman it worked but again not on frontend
i even tried to hard copy the same token from postman to action code and it still says the same error
I tried change the config code and added content-type = applicaton/json but all in vain.
so can you please help me. Thanks .if you are curious here is view:
#api_view(['POST'])
#csrf_exempt
#permission_classes([IsAuthenticated])
def add_to_wishlist(request, id):
product = get_object_or_404(Product, _id=id)
if product.users_wishlist.filter(id=request.user.id).exists():
product.users_wishlist.remove(request.user)
else:
product.users_wishlist.add(request.user)
return Response('your item is add to the wishlist ')
in frontend please try like this.
const config = {
method: 'post',
url: `/api/wishlist/add_to_wishlist/${id}/`,
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
},
data : {},
};
const { data } = await axios(config);
...

Nerlify: lambda response was undefined. check your function code again Response with status 500 in 583 ms

I'm trying to use netlify and its lambda function feature to run a node function. Based on https://css-tricks.com/using-netlify-forms-and-netlify-functions-to-build-an-email-sign-up-widget/.
Currently my netlify.toml has only the following:
[build]
functions = "./functions"
command = "npm run-script build"
mt submission.js:
// https://css-tricks.com/using-netlify-forms-and-netlify-functions-to-build-an-email-sign-up-widget/
require('dotenv').config();
const fetch = require('node-fetch');
console.log('to here');
const { LIST } = process.env;
const { B } = process.env;
exports.handler = async event => {
const email = JSON.parse(event.body).payload.data.EMAIL
console.log(email);
const asking = JSON.parse(event.body).payload.data.ASKING
console.log(asking);
var formData = {
'email': email,
'first_name': '',
'last_name': asking,
'lists[]': LIST
};
var encoded = Object.entries(formData).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join("&");
var endpoint = 'https://api.sendfox.com/contacts/?' + encoded;
const data = JSON.stringify(formData);
return fetch(endpoint, {
method: 'POST',
headers: {
Authorization: B,
'Content-Type': 'application/json',
},
body: JSON.stringify({ formData }),
})
.then(response => response.json())
.then(formData => {
console.log(`Submitted to Buttondown:\n ${formData}`)
})
.catch(error => ({ statusCode: 422, body: String(error) }))
// }
}
result:
Submitted to Buttondown:
[object Object]
◈ lambda response was undefined. check your function code again
Response with status 500 in 583 ms.
what am I doing wrong?
It's me, the author of the tutorial linked above!
Since I wrote the tutorial, node-fetch has updated with some breaking changes that make the code I wrote hard to debug.
A much more robust way to implement the same functionality in a netlify function would be to substitute the following:
const response = await fetch(
'https://api.buttondown.email/v1/subscribers',
{
method: 'POST',
headers: {
Authorization: `Token ${EMAIL_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ email }),
}
);
let responseText = await response.text();
console.log('response:', responseText);
return {
statusCode: response.status,
body: responseText,
};
This will return the proper status code, so you don't get the 'response was undefined' message. Plus, it'll properly log the response from buttondown, in case you need to do any further debugging.

Changing the Content-Type in axios header to fix a 415 error

I am trying to send a file to my back-end through an axios post request.
This is the error I currently have:
cherrypy._cperror.HTTPError: (415, 'Expected an entity of content
type application/json, text/javascript')
From what I have read I need to change the Content-Type in my post request, I looked around I am currently attempting to do so like this:
handleUploadButton(e){
const upload_file = this.state.file;
const formData = new FormData();
formData.append('file', upload_file);
const request = axios.post(someUrl, formData, {headers: {
"Content-Type": "application/json"}
})
.then(function (response) {
console.log('successfully uploaded', upload_file);
});
}
Not sure if relevant, but all this is happening through a reactjs form.
This is my current Content-Type: Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryBwjjjGuJEySeXdRU
I have no idea where to go from here. Any help would be greatly appreciated.
SignIn = () => {
console.log('login clicked')
let data = JSON.stringify({
password: this.state.password,
username: this.state.email
})
axios.post('url', data, {
headers: {
'Content-Type': 'application/json',
}
}
)
}
This worked for me:
const formData = new FormData();
formData.append('data', new Blob([JSON.stringify(data)], { type: 'application/json'}));
formData.append('file', file);
return axios.put(`${url}`, formData)
.then((response) => { console.log(response) })
.catch((error) => { console.log(error) })
I took this from another answer of a similar issue. You can check the original answer here.
You can fix different types catch() error by
.catch((error)=> {
if (error.response){
this.errors(error.response.message);
} else if (error.request) {
console.log('error.request');
} else {
console.log('Error', error);
}
console.log("rejected");
});
read more >>
In order to make axios include Content-Type: application-json you need to do that:
javascript
window.axios = require('axios')
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'

Fetch: POST JSON data

I'm trying to POST a JSON object using fetch.
From what I can understand, I need to attach a stringified object to the body of the request, e.g.:
fetch("/echo/json/",
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify({a: 1, b: 2})
})
.then(function(res){ console.log(res) })
.catch(function(res){ console.log(res) })
When using jsfiddle's JSON echo I'd expect to see the object I've sent ({a: 1, b: 2}) back, but this does not happen - chrome devtools doesn't even show the JSON as part of the request, which means that it's not being sent.
With ES2017 async/await support, this is how to POST a JSON payload:
(async () => {
const rawResponse = await fetch('https://httpbin.org/post', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({a: 1, b: 'Textual content'})
});
const content = await rawResponse.json();
console.log(content);
})();
Can't use ES2017? See #vp_art's answer using promises
The question however is asking for an issue caused by a long since fixed chrome bug.
Original answer follows.
chrome devtools doesn't even show the JSON as part of the request
This is the real issue here, and it's a bug with chrome devtools, fixed in Chrome 46.
That code works fine - it is POSTing the JSON correctly, it just cannot be seen.
I'd expect to see the object I've sent back
that's not working because that is not the correct format for JSfiddle's echo.
The correct code is:
var payload = {
a: 1,
b: 2
};
var data = new FormData();
data.append( "json", JSON.stringify( payload ) );
fetch("/echo/json/",
{
method: "POST",
body: data
})
.then(function(res){ return res.json(); })
.then(function(data){ alert( JSON.stringify( data ) ) })
For endpoints accepting JSON payloads, the original code is correct
I think your issue is jsfiddle can process form-urlencoded request only. But correct way to make json request is pass correct json as a body:
fetch('https://httpbin.org/post', {
method: 'POST',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
body: JSON.stringify({a: 7, str: 'Some string: &=&'})
}).then(res => res.json())
.then(res => console.log(res));
From search engines, I ended up on this topic for non-json posting data with fetch, so thought I would add this.
For non-json you don't have to use form data. You can simply set the Content-Type header to application/x-www-form-urlencoded and use a string:
fetch('url here', {
method: 'POST',
headers: {'Content-Type':'application/x-www-form-urlencoded'}, // this line is important, if this content-type is not set it wont work
body: 'foo=bar&blah=1'
});
An alternative way to build that body string, rather then typing it out as I did above, is to use libraries. For instance the stringify function from query-string or qs packages. So using this it would look like:
import queryString from 'query-string'; // import the queryString class
fetch('url here', {
method: 'POST',
headers: {'Content-Type':'application/x-www-form-urlencoded'}, // this line is important, if this content-type is not set it wont work
body: queryString.stringify({for:'bar', blah:1}) //use the stringify object of the queryString class
});
After spending some times, reverse engineering jsFiddle, trying to generate payload - there is an effect.
Please take eye (care) on line return response.json(); where response is not a response - it is promise.
var json = {
json: JSON.stringify({
a: 1,
b: 2
}),
delay: 3
};
fetch('/echo/json/', {
method: 'post',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
body: 'json=' + encodeURIComponent(JSON.stringify(json.json)) + '&delay=' + json.delay
})
.then(function (response) {
return response.json();
})
.then(function (result) {
alert(result);
})
.catch (function (error) {
console.log('Request failed', error);
});
jsFiddle: http://jsfiddle.net/egxt6cpz/46/ && Firefox > 39 && Chrome > 42
2021 answer: just in case you land here looking for how to make GET and POST Fetch api requests using async/await or promises as compared to axios.
I'm using jsonplaceholder fake API to demonstrate:
Fetch api GET request using async/await:
const asyncGetCall = async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = await response.json();
// enter you logic when the fetch is successful
console.log(data);
} catch(error) {
// enter your logic for when there is an error (ex. error toast)
console.log(error)
}
}
asyncGetCall()
Fetch api POST request using async/await:
const asyncPostCall = async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
// your expected POST request payload goes here
title: "My post title",
body: "My post content."
})
});
const data = await response.json();
// enter you logic when the fetch is successful
console.log(data);
} catch(error) {
// enter your logic for when there is an error (ex. error toast)
console.log(error)
}
}
asyncPostCall()
GET request using Promises:
fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => res.json())
.then(data => {
// enter you logic when the fetch is successful
console.log(data)
})
.catch(error => {
// enter your logic for when there is an error (ex. error toast)
console.log(error)
})
POST request using Promises:
fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
// your expected POST request payload goes here
title: "My post title",
body: "My post content."
})
})
.then(res => res.json())
.then(data => {
// enter you logic when the fetch is successful
console.log(data)
})
.catch(error => {
// enter your logic for when there is an error (ex. error toast)
console.log(error)
})
GET request using Axios:
const axiosGetCall = async () => {
try {
const { data } = await axios.get('https://jsonplaceholder.typicode.com/posts')
// enter you logic when the fetch is successful
console.log(`data: `, data)
} catch (error) {
// enter your logic for when there is an error (ex. error toast)
console.log(`error: `, error)
}
}
axiosGetCall()
POST request using Axios:
const axiosPostCall = async () => {
try {
const { data } = await axios.post('https://jsonplaceholder.typicode.com/posts', {
// your expected POST request payload goes here
title: "My post title",
body: "My post content."
})
// enter you logic when the fetch is successful
console.log(`data: `, data)
} catch (error) {
// enter your logic for when there is an error (ex. error toast)
console.log(`error: `, error)
}
}
axiosPostCall()
I have created a thin wrapper around fetch() with many improvements if you are using a purely json REST API:
// Small library to improve on fetch() usage
const api = function(method, url, data, headers = {}){
return fetch(url, {
method: method.toUpperCase(),
body: JSON.stringify(data), // send it as stringified json
credentials: api.credentials, // to keep the session on the request
headers: Object.assign({}, api.headers, headers) // extend the headers
}).then(res => res.ok ? res.json() : Promise.reject(res));
};
// Defaults that can be globally overwritten
api.credentials = 'include';
api.headers = {
'csrf-token': window.csrf || '', // only if globally set, otherwise ignored
'Accept': 'application/json', // receive json
'Content-Type': 'application/json' // send json
};
// Convenient methods
['get', 'post', 'put', 'delete'].forEach(method => {
api[method] = api.bind(null, method);
});
To use it you have the variable api and 4 methods:
api.get('/todo').then(all => { /* ... */ });
And within an async function:
const all = await api.get('/todo');
// ...
Example with jQuery:
$('.like').on('click', async e => {
const id = 123; // Get it however it is better suited
await api.put(`/like/${id}`, { like: true });
// Whatever:
$(e.target).addClass('active dislike').removeClass('like');
});
Had the same issue - no body was sent from a client to a server.
Adding Content-Type header solved it for me:
var headers = new Headers();
headers.append('Accept', 'application/json'); // This one is enough for GET requests
headers.append('Content-Type', 'application/json'); // This one sends body
return fetch('/some/endpoint', {
method: 'POST',
mode: 'same-origin',
credentials: 'include',
redirect: 'follow',
headers: headers,
body: JSON.stringify({
name: 'John',
surname: 'Doe'
}),
}).then(resp => {
...
}).catch(err => {
...
})
This is related to Content-Type. As you might have noticed from other discussions and answers to this question some people were able to solve it by setting Content-Type: 'application/json'. Unfortunately in my case it didn't work, my POST request was still empty on the server side.
However, if you try with jQuery's $.post() and it's working, the reason is probably because of jQuery using Content-Type: 'x-www-form-urlencoded' instead of application/json.
data = Object.keys(data).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(data[key])).join('&')
fetch('/api/', {
method: 'post',
credentials: "include",
body: data,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})
The top answer doesn't work for PHP7, because it has wrong encoding, but I could figure the right encoding out with the other answers. This code also sends authentication cookies, which you probably want when dealing with e.g. PHP forums:
julia = function(juliacode) {
fetch('julia.php', {
method: "POST",
credentials: "include", // send cookies
headers: {
'Accept': 'application/json, text/plain, */*',
//'Content-Type': 'application/json'
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" // otherwise $_POST is empty
},
body: "juliacode=" + encodeURIComponent(juliacode)
})
.then(function(response) {
return response.json(); // .text();
})
.then(function(myJson) {
console.log(myJson);
});
}
It might be useful to somebody:
I was having the issue that formdata was not being sent for my request
In my case it was a combination of following headers that were also causing the issue and the wrong Content-Type.
So I was sending these two headers with the request and it wasn't sending the formdata when I removed the headers that worked.
"X-Prototype-Version" : "1.6.1",
"X-Requested-With" : "XMLHttpRequest"
Also as other answers suggest that the Content-Type header needs to be correct.
For my request the correct Content-Type header was:
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
So bottom line if your formdata is not being attached to the Request then it could potentially be your headers. Try bringing your headers to a minimum and then try adding them one by one to see if your problem is resolved.
If your JSON payload contains arrays and nested objects, I would use URLSearchParams and jQuery's param() method.
fetch('/somewhere', {
method: 'POST',
body: new URLSearchParams($.param(payload))
})
To your server, this will look like a standard HTML <form> being POSTed.
You could do it even better with await/async.
The parameters of http request:
const _url = 'https://jsonplaceholder.typicode.com/posts';
let _body = JSON.stringify({
title: 'foo',
body: 'bar',
userId: 1,
});
const _headers = {
'Content-type': 'application/json; charset=UTF-8',
};
const _options = { method: 'POST', headers: _headers, body: _body };
With clean async/await syntax:
const response = await fetch(_url, _options);
if (response.status >= 200 && response.status <= 204) {
let data = await response.json();
console.log(data);
} else {
console.log(`something wrong, the server code: ${response.status}`);
}
With old fashion fetch().then().then():
fetch(_url, _options)
.then((res) => res.json())
.then((json) => console.log(json));
**//POST a request**
const createTodo = async (todo) => {
let options = {
method: "POST",
headers: {
"Content-Type":"application/json",
},
body: JSON.stringify(todo)
}
let p = await fetch("https://jsonplaceholder.typicode.com/posts", options);
let response = await p.json();
return response;
}
**//GET request**
const getTodo = async (id) => {
let response = await fetch('https://jsonplaceholder.typicode.com/posts/' + id);
let r = await response.json();
return r;
}
const mainFunc = async () => {
let todo = {
title: "milan7",
body: "dai7",
userID: 101
}
let todor = await createTodo(todo);
console.log(todor);
console.log(await getTodo(5));
}
mainFunc()
I think that, we don't need parse the JSON object into a string, if the remote server accepts json into they request, just run:
const request = await fetch ('/echo/json', {
headers: {
'Content-type': 'application/json'
},
method: 'POST',
body: { a: 1, b: 2 }
});
Such as the curl request
curl -v -X POST -H 'Content-Type: application/json' -d '#data.json' '/echo/json'
In case to the remote serve not accept a json file as the body, just send a dataForm:
const data = new FormData ();
data.append ('a', 1);
data.append ('b', 2);
const request = await fetch ('/echo/form', {
headers: {
'Content-type': 'application/x-www-form-urlencoded'
},
method: 'POST',
body: data
});
Such as the curl request
curl -v -X POST -H 'Content-type: application/x-www-form-urlencoded' -d '#data.txt' '/echo/form'
You only need to check if response is ok coz the call not returning anything.
var json = {
json: JSON.stringify({
a: 1,
b: 2
}),
delay: 3
};
fetch('/echo/json/', {
method: 'post',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
body: 'json=' + encodeURIComponent(JSON.stringify(json.json)) + '&delay=' + json.delay
})
.then((response) => {if(response.ok){alert("the call works ok")}})
.catch (function (error) {
console.log('Request failed', error);
});
// extend FormData for direct use of js objects
Object.defineProperties(FormData.prototype, {
load: {
value: function (d) {
for (var v in d) {
this.append(v, typeof d[v] === 'string' ? d[v] : JSON.stringify(d[v]));
}
}
}
})
var F = new FormData;
F.load({A:1,B:2});
fetch('url_target?C=3&D=blabla', {
method: "POST",
body: F
}).then( response_handler )
you can use fill-fetch, which is an extension of fetch. Simply, you can post data as below:
import { fill } from 'fill-fetch';
const fetcher = fill();
fetcher.config.timeout = 3000;
fetcher.config.maxConcurrence = 10;
fetcher.config.baseURL = 'http://www.github.com';
const res = await fetcher.post('/', { a: 1 }, {
headers: {
'bearer': '1234'
}
});

Categories

Resources