This question already has an answer here:
using of fetch API the unexpected end of input error occurr
(1 answer)
Closed 3 years ago.
On my client side, I simply want to aler the response I get from the server.
function displayItems()
{
fetch('http://ip_address:3000/users',{
method:'POST',
headers:{
'Accept':'application/json',
'Content-Type':'application/json',
},
mode:'no-cors'
})
.then((response) => {return response.json();})
.then((res) => { alert(res.message)})
}
On my server side, I have this simple code to respond to request
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.post('/', function(req, res, next) {
let obj = {message:'fsdfsdfsdfsd'}
res.send(obj);
console.log('server Reached')
});
module.exports = router;
After looking up other related problems, I am still unable to resolve this error: Uncaught (in promise) SyntaxError: Unexpected end of input.
Thank you in advance to those who look at this.
In addition to the no-cors problem Quentin pointed out with the duplicate (which he answers here), there are several other issues:
What you're sending isn't JSON:
res.send('Hello world'); // <=== This is plain text
...so response.json() would fail when trying to parse the response.
If you're just sending text like that, you'd use response.text() to read it instead of .json().
You're also not checking correctly for HTTP errors. It's not just you, almost everyone makes this mistake (which I've written up here), it's a flaw (IMHO) in the fetch API. To correctly check for errors and receive text (rather than JSON), see *** comments:
function displayItems()
{
fetch('http://172.30.117.7:3000/users',{
method:'POST',
headers:{
'Accept':'application/json',
'Content-Type':'application/json',
},
mode:'no-cors'
})
.then((response) => {
// *** Check for HTTP failure
if (!response.ok) {
throw new Error("HTTP status " + response.status);
}
// *** Read the text of the response
return response.text();
})
.then((message) => {
// *** Use the text
alert(message);
})
.catch((error) => {
/* ...*** handle/report error, since this code doesn't return the promise chain...*/
});
}
Alternately, if you wanted, you could send back JSON:
response.json({message: "Hi there"});
...and then on the client:
function displayItems()
{
fetch('http://172.30.117.7:3000/users',{
method:'POST',
headers:{
'Accept':'application/json',
'Content-Type':'application/json',
},
mode:'no-cors'
})
.then((response) => {
// *** Check for HTTP failure
if (!response.ok) {
throw new Error("HTTP status " + response.status);
}
// *** Read and parse the JSON
return response.json();
})
.then((res) => {
// *** Use the object
alert(res.message);
})
.catch((error) => {
/* ...*** handle/report error, since this code doesn't return the promise chain...*/
});
}
But again, all of that is aside from the primary problem Quentin pointed out with the duplicate.
Related
I have the following React Native client code:
confirmMatchRecord(userId, matchedUserData['userId'], matchRecordData['matchRecord']['matchWinner'], matchRecordData['matchType'], currentUserRating, matchedUserRating, matchData['_id'], matchRecordData['matchRecord']['_id'], airbnbRatingValue, true, new Date())
.then((results) => {
// Do stuff
})
.catch((error) => {
Alert.alert('Error', 'There was an issue with confirming the record. Please check your connection and/or try again later.');
});
And the following code in my confirmMatchRecord function:
export async function confirmMatchRecord(userId, matchedUserId, matchWinner, matchType, currentUserRating, matchedUserRating, matchId, matchRecordId, matchRating, matchConfirmed, timestamp) {
console.log('Attempting to record match');
info = { userId, matchedUserId, matchWinner, matchType, currentUserRating, matchedUserRating, matchId, matchRecordId, matchRating, matchConfirmed, timestamp }
const firebaseIdToken = await AsyncStorage.getItem('#firebaseIdToken')
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + firebaseIdToken },
body: JSON.stringify(info)
};
const response = await fetch(ngrokOrLocalhost + '/confirmmatchrecord', requestOptions)
if (response['Status']==='Failure') {
// throw new Error(`HTTP error! status: ${response.status}`);
throw new Error(400);
} else if (response['Status']==='Success') {
const data = await response.json()
return data
}
}
Server code:
router.post('/confirmmatchrecord', async (req, res) => {
// Do a lot of stuff
if (response==='Success') {
return res.status(200).json({'Status': 'Success'})
} else {
return res.status(400).json({'Status': 'Failure'})
console.log('Match record was not confirmed successfully');
}
When response['Status']==='Failure (sent by server) it throws an error 400 as you can see, I was hoping to trigger the .catch in the client code then. But that does not happen, because the client code continues to run on the .then part.
How should I do this instead? Not sure if using .catch here is even correct or if I should do this type of work another way.
You seem to be aware of the bit of a footgun in the fetch API (I write about it here) where fetch only rejects its promise on network errors, not HTTP errors, but your check is incorrect in a couple of ways:
It's status, not Status (capitalization matters), and
It's the HTTP code (400 for instance), not a string
The Response object provides a convenient ok flag that's true for any successful response and false otherwise, so:
const response = await fetch(ngrokOrLocalhost + '/confirmmatchrecord', requestOptions)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`); // This will be "HTTP error! status: 400" if the HTTP error code is 400
}
const data = await response.json();
return data;
In a comment you've said:
My response['Status'] was checking for a custom server message I had sent (res.status(400).json({'Status': 'Failure'}), I updated the post with it. Not sure why it didn't catch that
Ah! Okay. The reason it didn't catch it is that you're looking for it on the Response object, but your JSON is in the response body.
I suspect you don't want to use your own Status anymore since you know about response.ok and response.status now, but if you ever do want to include your own information in an error response as JSON, you can do that. You'd do it like this:
const response = await fetch(ngrokOrLocalhost + '/confirmmatchrecord', requestOptions)
const data = await response.json(); // Expects JSON in *both* the success response and the error response
if (data.Status === "Failure") {
throw new Error(`HTTP error! status: ${response.status}`); // This will be "HTTP error! status: 400" if the HTTP error code is 400
}
return data;
But I'd stick with just the built-in ok and status for pure success/failure information. This could be handy if you wanted to provide more details of the failure, though.
I have a JavaScript that makes a Fetch post call to the backend of the site. If the post-call goes well, the Fetch is able to handle the response. If something goes wrong in the post-call, the Fetch is not able to handle the error.
This are my codes:
async function startFetch(url, data_post){
return fetch(url, {
method: 'POST', // or 'PUT'
body: JSON.stringify(data_post), // data can be `string` or {object}!
headers:{
'Content-Type': 'application/json'
}
})
.then(response => response.json());
}
async function makeFetch(arrRows){
let url = 'somebackendpost';
for(const item of ArrRows){
let data_post = {};
data.name = item;
await startFetch(url, data_post)
.then(data => {
//when the fetch is okay, this part is executed
console.log(data);
//do other things like output to html
$('#result').append('<p>' + data.result + '</p>');
})
.catch(error ={
//a timeout 504 error occured, but this part seemed not to execute
console.log('error is', error);
//the code below is wrong (but has since been removed then)
//Is this the reason why the catch error part did not work?
//Since the code below has been removed, can I expect the catch error to now work?
$('#result').append('<p>' + data.result + '</p>');
});
}
}
function startProcess(){
//called by button click
let arrRows = ["Row1", "Row2", "Row3"];
makeFetch(arrRows);
}
At the time, the code was executed, there was a server issue. The browser console displayed a Gateway timeout error 504. Here is the console logs:
Fetch failed loading: POST "mysite/somebackendpost"
POST mysite/somebackendpost 504 (GATEWAY TIMEOUT)
error is SyntaxError: Unexpected end of JSON input
at fetch.then.response
Uncaught (in promise) ReferenceError: X is not defined
at startFetch.then.catch.error
Try updating your startFetch method to first check that the fetch response is "ok" before attempting to parse the response as json. This will catch most error scenarios (that are currently going undetected), before you attempt to parse json.
So, an update as follows should allow you to respond to errors correctly:
async function startFetch(url, data_post){
return fetch(url, {
method: 'POST', // or 'PUT'
body: JSON.stringify(data_post), // data can be `string` or {object}!
headers:{
'Content-Type': 'application/json'
}
})
.then(response => {
// Check that the response is valid and reject an error
// response to prevent subsequent attempt to parse json
if(!response.ok) {
return Promise.reject('Response not ok with status ' + response.status);
}
return response;
})
.then(response => response.json());
}
Hope this helps!
I am sending a status code 422 from my backend code with response body which contains the description of the error. I am using axios post as below to post a request:
post: function(url, reqBody) {
const request = axios({
baseURL: config.apiUrl,
url: url,
headers: {
'Content-Type': 'application/json',
'Authorization': sessionStorage.getItem('token')
},
method: 'POST',
data: reqBody,
responseType: 'json'
});
return request
.then((res) => {
return res;
})
.catch((error) => {
console.log(error);
return error;
})
}
The problem is when backend is returning error code 422, the error object I am catching has no information about response body. Is there any way I can retrieve the error text?
I had this same issue and the answer (as per Axios >= 0.13) is to specifically check error.response.data:
axios({
...
}).then((response) => {
....
}).catch((error) => {
if( error.response ){
console.log(error.response.data); // => the response payload
}
});
See here for more details.
The "body" of an AXIOS error response depends from the type of response the request had.
If you would like full details about this issue you can see this blogpost: How to catch the body of an error in AXIOS.
In summary AXIOS will return 3 different body depending from the error:
Wrong request, we have actually done something wrong in our request (missing argument, bad format), that is has not actually been sent. When this happen, we can access the information using error.message.
axios.get('wrongSetup')
.then((response) => {})
.catch((error) => {
console.log(error.message);
})
Bad Network request: This happen when the server we are trying to reach does not respond at all. This can either be due to the server being down, or the URL being wrong.
In this case, we can access the information of the request using error.request.
axios.get('network error')
.then((response) => {})
.catch((error) => {
console.log(error.request );
});
Error status: This is the most common of the request. This can happen with any request that returns with a status that is different than 200. It can be unauthorised, not found, internal error and more. When this error happen, we are able to grasp the information of the request by accessing the parameter specified in the snippets below. For the data (as asked above) we need to access the error.response.data.
axios.get('errorStatus')
.then((response) => {})
.catch((error) => {
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
})
For those using await/async and Typescript
try {
const response = await axios.post(url, body)
} catch (error) {
console.log(error.response.data);
}
For react native it just worked for me
api.METHOD('endPonit', body)
.then(response => {
//...
})
.catch (error => {
const errorMessage = JSON.parse(error.request.response)
console.log(errorMessage.message)
})
We can check error.response.data as #JoeTidee said. But in cases response payload is blob type? You can get error response body with the below code.
axios({
...
}).then((response) => {
....
}).catch(async (error) => {
const response = error.response
if(typeof response.data.text === function){
console.log(await response.data.text()); // => the response payload
} else {
console.log(response.data)
}
});
I am returning a string from backend but expecting a json as response type. So I need to return an object instead of string for axios to process it properly.
In my case I wanted to retrieve a response 404 error message (body).
I got body with error.response.data but I couldn't display it because the type was ArrayBuffer.
Solution:
axios.get(url, { responseType: 'arraybuffer' }).then(
response => {...},
error => {
const decoder = new TextDecoder()
console.log(decoder.decode(error.response.data))
}
)
Related posts:
Converting between strings and ArrayBuffers
I have the following code for making POST Requests.
I'm not 100% sure about error handling here, but it was important for me that I get body text when request is not successful.
One issue that I still do have is - if server responds with 200 OK but invalid json - can I log that payload?
What would be the correct way of logging for Fetch?
Fetch(data.notificationUrl, {
method: 'POST',
body: post_data,
headers: {
'Content-Type': 'application/json'
}
}).then((res) => {
if (!res.ok) {
// Could reject the promise here but than response text wouldn't be available
//return Promise.reject(`Response was not OK. Status code: ${res.status} text: ${res.statusText}`);
return res.text().then((txt) => `Response was not OK. Status code: ${res.status} text: ${res.statusText}.\nResponse: ${txt}`);
}
// response ok so we should return json, could follow https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch and determine the payload type by content-type header...
return res.json();
}).then((response) => {
if (response) {
// set result
// ...
// redirect
return reply.redirect(data.redirectUrlDirectory);
}
return reply(Boom.preconditionFailed(`Did not reply with correct payload! json:'${JSON.stringify(response)}'`));
}).catch((err) => {
return reply(Boom.badData(`Could not notify on url ${data.notificationUrl} about the payment ${id}.\nError: "${err}"`));
});
I would use something like this.
This fist option asumes your service response always the header "application/json" and a the pay load simple text which I mock it like this.
var app = new express();
app.get('/valid', function(req, res){
res.json({ok: "ok"});
});
app.get('/invalid', function(req, res){
res.json("bad json body");
});
and the fetch json handling should looks like this. The other part of your code looks like good for me.
var response2 = res.clone();
return res.json().then((json) => {
// log your good payload
try {
// here we check json is not an object
return typeof json === 'object' ? json : JSON.parse(json);
} catch(error) {
// this drives you the Promise catch
throw error;
}
}).catch(function(error) {
return response2.text().then((txt) => `Response was not OK. Status code: ${response2.status} text: ${response2.statusText}.\nResponse: ${txt}`);
//this error will be capture by your last .catch()
});
xxx.clone() allows you to resolve multiple times the same response and create your own combinations like the previous one.
I am working on Reactjs redux on front-end and Rails API as a back-end.
So now I call API with Fetch API method but the problem is I cannot get readable error message like what I got inside the network tabs
this is my function
export function create_user(user,userInfoParams={}) {
return function (dispatch) {
dispatch(update_user(user));
return fetch(deafaultUrl + '/v1/users/',
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify(userInfoParams)
})
.then(function(response) {
console.log(response);
console.log(response.body);
console.log(response.message);
console.log(response.errors);
console.log(response.json());
dispatch(update_errors(response));
if (response.status >= 400) {
throw new Error("Bad response from server");
}
})
.then(function(json){
console.log("succeed json re");
// We can dispatch many times!
// Here, we update the app state with the results of the API call.
dispatch(update_user(json));
});
}
}
But when errors came I cannot figure out how to get readable response message like I got when I check on my browser network tabs
So this is what I got from the network tabs when I got errors.
My console
This is my rails code
def create
user = User.new(user_params)
if user.save
#UserMailer.account_activation(user).deliver_now
render json: user, status: 201
else
render json: { errors: user.errors }, status: 422
end
end
But I cannot find out how can I get that inside my function
Since the text is hidden inside promise within response object, it needs to be handled like a promise to see it.
fetch(bla)
.then(res => {
if(!res.ok) {
return res.text().then(text => { throw new Error(text) })
}
else {
return res.json();
}
})
.catch(err => {
console.log('caught it!',err);
});
Similar to your answer, but with a bit more explanation... I first check if the response is ok, and then generate the error from the response.text() only for the cases that we have a successful response. Thus, network errors (which are not ok) would still generate their own error without being converted to text. Then those errors are caught in the downstream catch.
Here is my solution - I pulled the core fetch function into a wrapper function:
const fetchJSON = (...args) => {
return fetch(...args)
.then(res => {
if(res.ok) {
return res.json()
}
return res.text().then(text => {throw new Error(text)})
})
}
Then when I use it, I define how to handle my response and errors as needed at that time:
fetchJSON(url, options)
.then((json) => {
// do things with the response, like setting state:
this.setState({ something: json })
})
.catch(error => {
// do things with the error, like logging them:
console.error(error)
})
even though this is a bit old question I'm going to chime in.
In the comments above there was this answer:
const fetchJSON = (...args) => {
return fetch(...args)
.then(res => {
if(res.ok) {
return res.json()
}
return res.text().then(text => {throw new Error(text)})
})
}
Sure, you can use it, but there is one important thing to bare in mind. If you return json from the rest api looking as {error: 'Something went wrong'}, the code return res.text().then(text => {throw new Error(text)}) displayed above will certainly work, but the res.text() actually returns the string. Yeah, you guessed it! Not only will the string contain the value but also the key merged together! This leaves you with nothing but to separate it somehow. Yuck!
Therefore, I propose a different solution.
fetch(`backend.com/login`, {
method: 'POST',
body: JSON.stringify({ email, password })
})
.then(response => {
if (response.ok) return response.json();
return response.json().then(response => {throw new Error(response.error)})
})
.then(response => { ...someAdditional code })
.catch(error => reject(error.message))
So let's break the code, the first then in particular.
.then(response => {
if (response.ok) return response.json();
return response.json().then(response => {throw new Error(response.error)})
})
If the response is okay (i.e. the server returns 2xx response), it returns another promise response.json() which is processed subsequently in the next then block.
Otherwise, I will AGAIN invoke response.json() method, but will also provide it with its own then block of code. There I will throw a new error. In this case, the response in the brackets throw new Error(response.error) is a standard javascript object and therefore I'll take the error from it.
As you can see, there is also the catch block of code at the very end, where you process the newly thrown error. (error.message <-- the error is an object consisting of many fields such as name or message. I am not using name in this particular instance. You are bound to have this knowledge anyway)
Tadaaa! Hope it helps!
I've been looking around this problem and has come across this post so thought that my answer would benefit someone in the future.
Have a lovely day!
Marek
If you came to this question while trying to find the issue because response.json() throws "Unexpected token at position..." and you can't find the issue with the JSON, then you can try this, basically getting the text and then parsing it
fetch(URL)
.then(async (response) => {
if (!response.ok) {
const text = await response.text()
throw new Error(text)
}
// Here first we convert the body to text
const text = await response.text()
// You can add a console.log(text), to see the response
// Return the JSON
return JSON.parse(text)
})
.catch((error) => console.log('Error:', error))
.then((response) => console.log(response))
I think you need to do something like this
export function create_user(user,userInfoParams={}) {
return function (dispatch) {
dispatch(update_user(user));
return fetch(deafaultUrl + '/v1/users/',
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify(userInfoParams)
})
.then(function(response) {
console.log(response);
console.log(response.body);
console.log(response.message);
console.log(response.errors);
console.log(response.json());
return response.json();
})
.then(function(object){
if (object.errors) {
dispatch(update_errors(response));
throw new Error(object.errors);
} else {
console.log("succeed json re");
dispatch(update_user(json));
}
})
.catch(function(error){
this.setState({ error })
})
}
}
You can access the error message with this way:
return fetch(deafaultUrl + '/v1/users/',
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify(userInfoParams)
})
.then(function(response) {
console.log(response);
console.log(response.body);
console.log(response.message);
console.log(response.errors);
console.log(response.json());
dispatch(update_errors(response));
if (response.status >= 400) {
throw new Error("Bad response from server");
}
})
.then(function(json){
console.log("succeed json re");
// We can dispatch many times!
// Here, we update the app state with the results of the API call.
dispatch(update_user(json));
})
// here's the way to access the error message
.catch(function(error) {
console.log(error.response.data.message)
})
;
The best choice is not to catch the error in the fetch because this will be useless:
Just in your api put a response with not code error
static GetInvoicesAllData = async (req,res) =>
{
try{
let pool = await new Connection().GetConnection()
let invoiceRepository = new InvoiceRepository(pool);
let result = await invoiceRepository.GetInvoicesAllData();
res.json(result.recordset);
}catch(error){
res.send(error);
}
}
Then you just catch the error like this to show the message in front end.
fetch(process.env.REACT_APP_NodeAPI+'/Invoices/AllData')
.then(respuesta=>respuesta.json())
.then((datosRespuesta)=>{
if(datosRespuesta.originalError== undefined)
{
this.setState({datosCargados:true, facturas:datosRespuesta})
}
else{ alert("Error: " + datosRespuesta.originalError.info.message ) }
})
With this you will get what you want.
You variables coming back are not in response.body or response.message.
You need to check for the errors attribute on the response object.
if(response.errors) {
console.error(response.errors)
}
Check here https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
You should actually be returning an error response code from the server and use the .catch() function of the fetch API
First you need to call json method on your response.
An example:
fetch(`${API_URL}`, {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(userInfoParams)
})
.then((response) => response.json())
.then((response) => console.log(response))
.catch((err) => {
console.log("error", err)
});
Let me know the console log if it didn't work for you.