In my ASP.NET Core API backend, I send Status 204 when there's no data but I noticed in the front end, my fetch call is still showing response.ok.
Two questions:
Is this normal behavior? I guess, it was a successful call so response could be OK, but it just threw me off.
What's the best way to check for Status 204?
My current code in my React/Redux app looks like this:
export const apiCall = () => {
return (dispatch) => fetch("/api/get", fetchOptions)
.then((response) => {
if(response.ok) {
// Do something
} else {
// Couldn't get data!
}
})
};
This is my standard code block in handling fetch calls. How should I modify it to handle Status 204 scenarios?
In addition to checking Response.ok, you can also check Response.status. Per MDN:
The status read-only property of the Response interface contains the status code of the response (e.g., 200 for a success).
Response.ok is only a check to see if the the status property is 200-299.
So, rather than just checking ok, you could do:
if (response.status === 200) {
// Do something
} else if (response.status === 204) {
// No data!
} else {
// Other problem!
}
Related
I am making a GET request to an API that returns JSON. The response is usually JSON with a status 200. However, in rare cases it returns a status 300 with JSON. That 300 response gets caught in my React app as a ResponseError.
I am able to see the contents of the body in the network tab, and console.log(e.response.body) shows that it's a ReadableStream, but e.response.body.json() throws a TypeError.
The 200 status returns an object like:
{
user: { ... }
}
The 300 status returns an object like the following that can only be seen in the network tab:
{
users: {
user1: { ... },
user2: { ... }
}
How can I access the contents of the response body in my React app? The GET is made with fetch. Here's a simple example of what's happening:
try {
const res = await fetch('/api/user')
const data = await res.json()
// status 200 always works
} catch(e) {
// 300 status always goes here
// e.response.body is a ReadableStream
const await res = e.response.body.json() // throws a TypeError
}
In the catch block, I tried e.response.body.json(). That throws a TypeError.
You can avoid try catch block and use fetch API only as
fetch('/api/user')
.then((response) => {
if(response.status == 300 || response.status == 200){
return res.json()
}
})
.then((data) => console.log(data))
.catch((error) => {
console.error('Error:', error);
});
Is it necessary to check if axios.get() response status is 200 ?
useEffect(() => {
const fetch = async () => {
try {
const response = await axios.get('/api');
if (response.status === 200) {
setState(response.data);
}
} catch (error) {
console.error(error);
}
};
fetch();
}, []);
or I can do this
useEffect(() => {
const fetch = async () => {
try {
const { data } = await axios.get('/api');
setState(data);
} catch (error) {
console.error(error);
}
};
fetch();
}, []);
if it is, what is best practice?
Normally you don't need to check. But technically, it totally depends on how your team interpret HTTP protocol.
Even though it’s discouraged, I’ve seen teams that totally disregard the standard semantics of HTTP status code, blindly set status code to 200 in almost all cases, then encode the success/failure state within data payload, e.g. data.success == true or false. In that case checking status code means nothing.
Axios is just a library that facilitates message exchanging over HTTP. The real working horse is HTTP protocol. You can even customize axios to determine what case is deemed "error", like shown in this question. Better consult your backend colleagues to understand their server response convention and reach consensus within your team.
I'm creating an api test framework for a project I am working on and I'm looking to validate required fields in JSON objects being sent to an endpoint.
I'm trying to send a JSON object with a missing field and expecting a 400 response from my application with a validation message. But when making this call with axios it (somewhat rightly) throws an error as it received the 400.
I want to be able to assert that 400 is expected and to assert on the validation message.
All the examples I've come across are all regarding dealing with the 400 response in the correct way you would if you are not expecting the 400 response, but i am expecting it.
I couldn't seem to find anyone else trying this.
async function createReminder(reminderObject) {
const response = await axios.post(`${config.get('baseUrl')}/reminder`, {
...reminderObject
});
return response;
}
module.exports.createReminder = createReminder;
Here is my working code as it stands. This will make a valid 200 when being used with a valid call.
I need to have the 400 response / validation message be returned in teh response object to be handled in the function that calls this function.
In effect, you want to intercept the response and transform it as you wish. There's a section in the axios docs dedicated to interceptors. As an example of what you can do with it, here is what I've used in a recent project with a Laravel back-end:
axios.interceptors.response.use(
res => Promise.resolve(res.data),
error => {
let message = null;
if (error.response) {
// if error has a data.error property, it's an error formatted by server
if (error.response.data.error) message = error.response.data.error;
else if (error.response.status === 500) message = 'Oops! Something went wrong.';
} else {
// if error has a code property, it's an error defined by axios
if (error.code === 'ECONNABORTED') message = 'Timeout exceeded';
else message = 'Oops! Something went wrong.';
}
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject({ error: message });
}
);
The code above allows me to make axios calls as follows, and always expect the same format:
axios.post('/url', { data: { ... }})
.then(({ jsonProp1 }) => console.log(jsonProp1))
.catch(({ error }) => console.log(error);
Note: if you do create a new axios instance, you need to reinject the response interceptor for this instance too.
could you please tell me how to show error message in react js when http request send ?
I make a service in the nodejs where I am sending 400 status with error message . I want to show this error message on frontend.
app.get('/a',(req,res)=>{
res.status(400).send({message:" some reason error message"})
})
Now I want to show this error message on frontend .on catch I will not get this message.
try {
const r = await axios.get('http://localhost:3002/a');
} catch (e) {
console.log('===============================================')
console.log(e)
console.log(e.data)
hideLoading();
setErrorMessage(e.message);
showErrorPopUp();
}
on catch i will not get this message.getting on stack of error
[![enter image description here][1]][1]
It's better to respond with a JSON in this particular case from the server:
app.get('/a',(req,res) => {
res.status(400).json({message:"some reason error message"})
})
So in the client, you can read from error.response easily
try {
const r = await axios.get('http://localhost:3002/a');
} catch (e) {
if (e.response && e.response.data) {
console.log(e.response.data.message) // some reason error message
}
}
Read more about handling caught errors in axios here
That's a very subjective question. You might need to use some middleware to handle async actions in a better way like redux-saga or redux-thunk.
The approach would be define a error state in your store. And, when you get an error update the state, dispatching an action.
And, in your component (container), you need to have an observer to get the updated error state.
try {
const r = await axios.get('http://localhost:3002/a');
} catch (e) {
if (e.response && e.response.data) {
// Dispatch an action here
console.log(e.response.data.message) // some reason error message
}
}
For reference, there is a very basic and good tutorial by Dan.
https://egghead.io/lessons/javascript-redux-displaying-error-messages
Axios have validateStatus in request-config where you can whitelist your status
https://github.com/axios/axios#request-config
// validateStatus defines whether to resolve or reject the promise
for a given // HTTP response status code. If validateStatus
returns true (or is set to null // or undefined), the promise
will be resolved; otherwise, the promise will be // rejected.
axios
.get("<URL>",{validateStatus: function (status) {
return (status >= 200 && status < 300) || status==400;
}})
.then(function(response) {
// handle success;
})
.catch(function(response) {
// handle error
})
.finally(function(error) {
// always executed
}); ```
Is there a way to hook into all of the fetch API (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) ajax call errors similar to jquery's $(document).ajaxError()?
We're using $(document).ajaxError() to detect Session Timeout/failures and present a Login dialog. I would like to detect timeouts as soon as I get a failed response.
Here's some code:
$(document).ajaxError((event, jqxhr) => {
if (jqxhr.statusText === 'abort') {
return;
}
// Sometimes (even with /restapi/session), we get no jqxhr.responseJSON, but we do have jqxhr.responseText
let responseJSON = jqxhr.responseJSON;
if (!responseJSON && jqxhr.responseText) {
responseJSON = JSON.parse(jqxhr.responseText);
}
if (jqxhr.status === 401) {
if (!responseJSON) {
// https://sentry.zetta.net//zetta/prod-frontend/group/13631/
alert('API call\'s responseJSON is empty, and so is jqxhr.responseText. Check the Console logs for more info.');
console.error('jqxhr:', jqxhr, 'event:', event);
}
if (responseJSON.code === 1900) { // session expired
this.setState({ showExpirationModal: true });
} else {
// TODO: consider removing handling of these errors locally
// Currently we'd be showing 2 popups in some places: one with a "permission denied", and another with this error handler
// alert('Unauthorized');
}
} else if (jqxhr.status === 500) { // TODO: see if we should to all 500 errors in this global handler
if (responseJSON.code === 8017) { // maintenance mode
alert(responseJSON.message);
}
} else if (!navigator.onLine) {
this.setState({ showConnectionModal: true });
} else if (responseJSON === undefined) {
console.error(event, jqxhr);
}
}
I'm not sure if the Fetch API supports this.
From the document linked at OP
Note that the fetch specification differs from jQuery.ajax() in
mainly two ways that bear keeping in mind:
The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, it will
resolve normally (with ok status set to false), and it will only
reject on network failure or if anything prevented the request from
completing.
To get the status code, return Response.status
fetch("/path/to/server"/*, options object*/)
.then(response => ({headers: response.headers, code: response.status}))
.then(({headers, code}) => {
for (let [key, value] of headers) {
console.log(key, value)
}
console.log(code); // `403` at linked jsfiddle
// if (code === /* code */)
// do stuff
})
.catch(err => console.log(err));
jsfiddle https://jsfiddle.net/wgwd9hk3/