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.
Related
I'm trying to implement validation on my Node.js back-end so whenever the data doesn't pass the validation, I'm sending this to the front-end:
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
so that I could render the errors on the front-end. Sadly, when I open the console, I only see:
POST http://localhost:3000/login 400 (Bad Request)
as opposed to an object that would contain config, data, headers, request and status. So I am wondering how the hell am I supposed to access the errors object I'm returning to the front-end. I'm following express-validator's docs and this is how they do it as well - https://express-validator.github.io/docs/index.html
You just need to retrieve and parse the response body, even for non-successful requests.
Here's an example using fetch but the approach would be similar using other libs
const doFetch = async (url) => {
const res = await fetch(url, {
// method, headers, body, etc
})
if (!res.ok) {
if (res.status === 400) {
throw await res.json() // this will parse the JSON response body
}
// handle other errors
throw { errors: [ res.statusText ] } // conform to a standard format
}
// handle success
}
doFetch('http://example.com').catch(({ errors }) => {
console.error(errors)
})
Using axios, I'm making GET requests to multiple links to test to see if they are broken links. If the GET requests returns an error, I want to log this error to the console and send it to the client side with socket. However, on some links (not all), I get an UnhandledPromiseRejectionWarning.
// check if the link is dead
axios.get(linkHref, {
auth: {
username: USERNAME,
password: PASSWORD
}
})
.then( (response) => {
if (response.status != 200) {
resultData = {
text: linkText,
url: linkHref,
statusCode: String(response.status)
}
console.log(resultData);
io.sockets.emit("result", resultData); // send link to client-side
}
})
.catch( (error) => {
if (error) {
resultData = {
text: linkText,
url: linkHref,
statusCode: String(error.response.status) // this is where the error is
}
console.log(resultData);
io.sockets.emit("result", resultData); // send link to client-side
}
});
I expect it work correctly and return the status code of the error but sometimes I get a UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'status' of undefined error. But with my if statement in the .catch, I am checking to make sure that there is an error and that it is not undefined so I don't understand why I'm getting this error.
EDIT: Also, this works (does not show me that error) when I run it on my Mac but not in Windows ... why?
TypeError: Cannot read property 'status' of undefined The response property on error.response is not defined eg error = { response: undefined }. Therefore, referencing any properties on it throws an error. error.response[property]
Also on failed/hanging http requests the error argument is different. Checkout this gist axios-catch-error. The error argument will have different properties for this case.
hey can you check by validating whether you are getting the response property on the error object in the if else condition
.catch( (error) => {
if (error.response) {
//rest of the code...
}
});
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
}); ```
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!
}
I am not sure what is going on here, but when I am sending back a 400, or some other error request back to the client, node is setting a generic message instead of the one I am sending back. Here is a snippet from my server and what gets put in the UI on the client
Server:
var noContentFound = {
error:true,
message:'No data found for this search'
}
res.status(400).send(noContentFound);
return;
Client:
.catch(function (error) {
let errorFromServer = {
generalServerError:{
error:true,
message:error.message
},
...
};
return errorFromServer;
//both error, and error.message give the message "Request failed with status code 400"
});
So I am sending back in my server noContentFound object with the error message in it through res.status(400).send(noContentFound); But in the client the only thing that is returned in the catch function is Request failed with status code 400 I dont have access to the message I am trying to send back.
Odd thing is, in the network tab on that call on the response tab I can see the object coming through. But error in the catch function does not give me access to that object.
Anyone know why?
EDIT:
Client Call looks like this:
Base Module:
getContent(obj, envBool, baseUrl, httpRequest)
.then(function(data){
//assign to my store object in react and emit the change
});
Where the call is made module:
const getContent(obj, envBool, baseUrl, httpRequest){
//set some variables and data to send
return httpRequest.post(enviUrl,objToSend)
.then(function(response){
returnObj = {
//assign returned data...
}
//...
return returnObj;
})
.catch(function(error){
returnObj = {
//assign and handle error...
}
//...
return returnObj;
});
}