Angular2 httpClient - How can you get the content body? - javascript

Before I updated to HttpClient, I used the Http class and loaded data using the following code.
get(): Promise<Account> {
return this.http.get(this.apiUrl)
.toPromise()
.then((response) => response.json())
.catch(this.handleError);
}
private handleError(error: any) {
// do nothing for unauthorized user, as should be handles on component
if (error.status != '401') {
console.error('An error occurred', error);
}
return Promise.reject(error._body || error);
}
If the server errored with a 500, or 400 (validation) i could return the text from the server via my .catch handler.
Now I have updated to use the HttpClient and my code has changed to this
get(): Promise<Account> {
return this.httpClient.get<Account>(this.apiUrl)
.toPromise()
.catch(this.handleError);
}
private handleError(error: any) {
// do nothing for unauthorized user, as should be handles on component
if (error.status != '401') {
console.error('An error occurred', error);
}
return Promise.reject(error._body || error);
}
As you can see only the get method has changed, but now I cannot get the _body of the error response?
How can I get the body of the response like I did before?

Related

When should one include the error handler on an Observable?

I'm confused about general good practice when it comes to error handling. For example, if I'm already catching the error in my service, do I still need to include the error handler in my subscription?
Here's my http method in my service. As you can see it calls catchError:
deleteTask(id: number): Observable<any>{
return this.http.delete(this.tasksUrl+'/'+`${id}`)
.pipe(
catchError(this.handleError)
);
}
private handleError(res: HttpErrorResponse | any) {
console.error(res.error || res.body.error);
return observableThrowError(res.error || 'Server error');
}
And in my component:
delete(id: number){
this.deleteService.deleteTask(id).subscribe(
(val) => {
/*post processing functionality not relevant to this question
*/
}
);
}
In the angular documentation https://angular.io/guide/observables the error handler is described as optional:
myObservable.subscribe(
x => console.log('Observer got a next value: ' + x),
err => console.error('Observer got an error: ' + err),
() => console.log('Observer got a complete notification')
);
So in my example, would including the error handler on my subscription add anything to my code? Like if I did:
delete(id: number){
this.deleteService.deleteTask(id).subscribe(
(val) => {
/*post processing functionality not relevant to this question
*/
},
err => console.error('Observer got an error: ' + err)
);
Would it catch anything my catchError didn't catch? It almost feels like it would be good practice to always include the error handler, so I don't know why it's marked as optional? When should one use the subscription error handler vs other forms of error handling?
It's all about how you want to handle error in your application,
if you want to throw a fancy error instead of the actual error comming back from server, you can have a error handler in service end and who ever consume your service will get the fancy error instead of actual error.
// copied from your question
deleteTask(id: number): Observable<any>{
return this.http.delete(this.tasksUrl+'/'+`${id}`)
.pipe(
catchError(this.handleError)
);
}
private handleError(res: HttpErrorResponse | any) {
console.error(res.error || res.body.error);
return observableThrowError(res.error || 'Server error');
}
if not, you dont need to handle error in your service code, let the consumer(service/component) handle it.
deleteTask(id: number): Observable<any>{
return this.http.delete(this.tasksUrl+'/'+`${id}`);
}
// component
...
this.service.deleteTask(id).subscribe(success,(err) => {
// example
alert(err.message);
});
...
To handle common http errors (500, 401, 403, 404) you can write a HttpInterceptor, so that you dont need to write the error handling logic everywhere.
import { Injectable } from '#angular/core';
import {
HttpEvent, HttpRequest, HttpHandler, HttpInterceptor, HttpErrorResponse
} from '#angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
#Injectable()
export class MyAppHttpInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 401) {
// redirect to login page
} else {
return throwError(error);
}
})
);
}
}
if you want to log the errors to server or if you want to show a custom console or error notification over the screen on development mode or for debugging, you can create a global error handler service extending the existing ErrorHandler servcie in angular.
import { ErrorHandler } from '#angular/core';
#Injectable()
export class GlobalErrorHandler implements ErrorHandler {
handleError(error) {
// your custom error handling logic
}
}
Would it catch anything my catchError didn't catch?
No, you are just passing the same error through. But if your subscription doesn't have an error handler you will get an exception if you don't handle it. So you should either have an error handler on your subscription or pass an observable with no data.
const { throwError, of } = rxjs;
const { catchError } = rxjs.operators;
throwError('error').pipe(catchError(error => {
console.log('Caught error - ', error);
return of(null);
})).subscribe(val => { console.log('No error handler needed'); });
throwError('error').pipe(catchError(e => {
console.log('Caught error - ', e);
return throwError(e);
})).subscribe(val => {}, error => { console.log('Subscription handled error - ', error); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>

What's the best way to deal with an error in the server side and in the client side using nodejs + express

I'd like to know the best way to deal with errors in a response - request.
I have this route that receive a request:
app.get('/getInfo', function (req, res, next) {
let obj = {}
try {
obj = {
...
date: lastUpdatedDate('./utils/appVersion.js'),
...
}
res.status(200).send(obj)
} catch (error) {
console.log(error.message)
res.send({error: "The data wasn't load"})
}
})
And this function where the request is made
getInfo () {
axios.get(process.env.REACT_APP_HOST + '/getInfo')
.then(resp => {
this.appInfoHandler(resp.data)
})
.catch(function (error) {
console.log(error)
})
}
What's the best way to deal with the error if it occurs in the server side?
Let's supose that in this code block the directory doesn't exists: lastUpdatedDate('./directoreyDoesntExists/appVersion.js'),
So my code goes to the catch block.
Should I send the error like this:
res.send({error: "The data wasn't load"})
Should I set a status like this?
res.status(500).send({error: "The data wasn't load"})
Or should I set a status with a different status code?
Based on that, what's the best way to deal with it in my frontend method getInfo() to get the error and show the error message on web interface?
Should I do an if else inside the .then block like this?
getInfo () {
axios.get(process.env.REACT_APP_HOST + '/getInfo')
.then(resp => {
if(resp.status === 200){
this.appInfoHandler(resp.data)
}else if (resp.status === 400){
//print error message on web interface
}else if (resp.status === 500){
//print error message on web interface
})
.catch(function (error) {
console.log(error)
})
Or should I deal with this error directly in the catch block like this
getInfo () {
axios.get(process.env.REACT_APP_HOST + '/getInfo')
.then(resp => {
this.appInfoHandler(resp.data)
})
.catch(function (error) {
//print error message on web interface
})
}
For this case
res.send({error: "The data wasn't load"})
vs
res.status(500).send({error: "The data wasn't load"})
send a status is just more detailed, but both are ok.
check Proper way to set response status and JSON content
For this case, depends on what you need
then(resp => {
if(resp.status === 200){
this.appInfoHandler(resp.data)
}else if (resp.status === 400){
//print error message on web interface
}else if (resp.status === 500){
//print error message on web interface
})
.catch(function (error) {
console.log(error)
})
vs
getInfo () {
axios.get(process.env.REACT_APP_HOST + '/getInfo')
.then(resp => {
this.appInfoHandler(resp.data)
})
.catch(function (error) {
//print error message on web interface
})
}
You can handle all the errors sending them to the catch block
else if (resp.status === 400){
//print error message on web interface
not printing the error in here but throwing a new error that will be send it to the catch block
throw new ApiError("UserNotFount",400,"not found");
throw new Error('Error 400, not found');
For this case
res.send({error: "The data wasn't load"})
vs
res.status(500).send({error: "The data wasn't load"})
I would suggest sending error as well as status code because that will be more descriptive for the client.
and for the second case
getInfo () {
axios.get(process.env.REACT_APP_HOST + '/getInfo')
.then(resp => {
if(resp.status === 200){
this.appInfoHandler(resp.data)
}else if (resp.status === 400){
//print error message on web interface
}else if (resp.status === 500){
//print error message on web interface
})
.catch(function (error) {
console.log(error)
})
vs
getInfo () {
axios.get(process.env.REACT_APP_HOST + '/getInfo')
.then(resp => {
this.appInfoHandler(resp.data)
})
.catch(function (error) {
//print error message on web interface
})
}
In this case I would suggest to use the catch block directly whenever you get an error because response status depends on error but not the other way around
As a beginner working on a REST Api, you should take a look at a guidelines - microsoft's are pretty legit: https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design.
Basically, you need to return the correct HTTP code for each request, take a look at https://http.cat/ - for example if the request is malformed, return 400, and if the user is unauthorized return 401:
if (!req.body.name) {
res.status(400).send({ error: 'missing user name' }); // 400 bad request
}
const user = getUser(req.body.name, req.body.pass);
if(!user) {
res.status(401).send({ error: 'user does not exist' }); // 401 unauthorized
}
try {
const token = createToken(user);
// better to set a cookie
res.status(200).send({ token }); // 200 success
} catch(e) {
res.status(500).send({ erroe: e.message }); // 500 internal error
}
if(isTeapot) {
res.status(418).send({ error: 'I can only make tea' }); // 418 teapot, totally real
}
To make things easier there are a lot of libraries to help you generate better error messages and handle errors better, one of my favorites is celebrate
Any status code other that 200 would mean unsuccessful so you dont need to use those if-else statements. The better alternative is to catch the error and send it with response as it is. The benefit is that you would receive the type of error occured without hardcoding the status codes.
(for ex, we take the status code here to be 400 unsuccessful)
.catch(function (error) {
//print error message on web interface
res.status(400).send(JSON.stringify(error, undefined, 2));
});
By using the stringify method you can print the exact error on the console also.
.catch(function (error) {
console.log(JSON.stringify(error, undefined, 2));
});
The parameters in the stringify method here are:
error object
undefined: The array which contains the keys for filtering the keys in the object(here, error). All those keys present in this array are only the ones not filtered out.
2: It is used to introduce whitespace in object representation

Angular 5 HttpClient Error response not catchable

We're working with Angular 5 and a Spring 2 OAuth Backend.
Now when I send an old token it's of course expired. It returns status code: 401 and an error response with invalid token and so on. Now I can't see it in my logs or when I catch the error. I want to get the error so I can at first log it and later on either refresh the token or send him to the Login Page.
Now if i subscribe to the request with:
.subscribe(res => {
//just random stuff.
}, err => {
console.log("error", err);
});
I just see this response in the log with an unknown error like in this image
Could it be failure of the backend? Because i also see in the logs something like a "No 'Access-Control-Allow-Origin' header is present"-error, although it's because of the invalid token.
Although I can see this response code in Google Chrome Dev Tools
and a 401 status code.
So I tried to find a solution myself. I've already got an interceptor and tried it with some solutions
return next.handle(authReq)
.catch(error => {
console.log("im in here");
console.log(error);
return Observable.throw(error);
});
The Http Service just throws an error that catch is not a function without even logging the error or the "im in here".
I have also tried with the .do after next.handle and I got the same error like catch
.do((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
// do stuff with response if you want
}
}, (err: any) => {
console.log(err);
if (err instanceof HttpErrorResponse) {
if (err.status === 401) {
}
}
});
I've tried with pipe after the http.get but it doesn't work either.
http.get(...).pipe(
retry(3), // retry a failed request up to 3 times
catchError(this.handleError) // then handle the error
);
import 'rxjs/add/operator/catch';
Somefunc(){
this.httpClient
.get("data-url")
.subscribe(
data => console.log('success', data),
error => console.log('oops', error)
);
}
OR
this.httpClient
.get("data-url")
.catch((err: HttpErrorResponse) => {
// simple logging, but you can do a lot more, see below
console.error('An error occurred:', err.error);
});
Should work.

Angular 2 - Http client rejects promise when status code is 404

I recently started using Angular 2 and quickly discovered that when utilizing the HTTP client that a return code of 404 from the server ends up rejecting the promise.
Here's the code I have in my service:
export class CalculatorService {
private _serviceUrl = 'http://localhost:3000';
private headers = new Headers({'Content-Type': 'application/json'});
constructor(private http: Http) { }
getCode(request: CalculatorRequest) {
return this.http.post(this._serviceUrl, JSON.stringify(request), {headers: this.headers})
.toPromise()
.then(response => response.json() as CalculatorResponse)
.catch(this.handleError);
}
private handleError(error): Promise<any> {
const errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server Error : Service Unavailable';
if (errMsg != null) {
return Promise.reject(errMsg);
}
}
}
Unfortunately, instead of resolving the promise, my handleError method is called as the 404 causes a rejection.
Is there a straightforward way to resolve a request with a return code of 404?
Thanks!

How to catch and handle error response 422 with Redux/Axios?

I have an action making a POST request to the server in order to update a user's password, but I'm unable to handle the error in the chained catch block.
return axios({
method: 'post',
data: {
password: currentPassword,
new_password: newPassword
},
url: `path/to/endpoint`
})
.then(response => {
dispatch(PasswordUpdateSuccess(response))
})
.catch(error => {
console.log('ERROR', error)
switch (error.type) {
case 'password_invalid':
dispatch(PasswordUpdateFailure('Incorrect current password'))
break
case 'invalid_attributes':
dispatch(PasswordUpdateFailure('Fields must not be blank'))
break
}
})
When I log the error this is what I see:
When I check the network tab I can see the response body, but for some reason I can't access the values!
Have I unknowingly made a mistake somewhere? Because I'm handling other errors from different request fine, but can't seem to work this one out.
Example
getUserList() {
return axios.get('/users')
.then(response => response.data)
.catch(error => {
if (error.response) {
console.log(error.response);
}
});
}
Check the error object for response, it will include the object you're looking for so you can do error.response.status
https://github.com/mzabriskie/axios#handling-errors
Axios is probably parsing the response. I access the error like this in my code:
axios({
method: 'post',
responseType: 'json',
url: `${SERVER_URL}/token`,
data: {
idToken,
userEmail
}
})
.then(response => {
dispatch(something(response));
})
.catch(error => {
dispatch({ type: AUTH_FAILED });
dispatch({ type: ERROR, payload: error.data.error.message });
});
From the docs:
The response for a request contains the following information.
{
// `data` is the response that was provided by the server
data: {},
// `status` is the HTTP status code from the server response
status: 200,
// `statusText` is the HTTP status message from the server response
statusText: 'OK',
// `headers` the headers that the server responded with
headers: {},
// `config` is the config that was provided to `axios` for the request
config: {}
}
So the catch(error => ) is actually just catch(response => )
EDIT:
I still dont understand why logging the error returns that stack message. I tried logging it like this. And then you can actually see that it is an object.
console.log('errorType', typeof error);
console.log('error', Object.assign({}, error));
EDIT2:
After some more looking around this is what you are trying to print. Which is a Javascipt error object. Axios then enhances this error with the config, code and reponse like this.
console.log('error', error);
console.log('errorType', typeof error);
console.log('error', Object.assign({}, error));
console.log('getOwnPropertyNames', Object.getOwnPropertyNames(error));
console.log('stackProperty', Object.getOwnPropertyDescriptor(error, 'stack'));
console.log('messageProperty', Object.getOwnPropertyDescriptor(error, 'message'));
console.log('stackEnumerable', error.propertyIsEnumerable('stack'));
console.log('messageEnumerable', error.propertyIsEnumerable('message'));
Here is the proper way to handle the error object:
axios.put(this.apiBaseEndpoint + '/' + id, input)
.then((response) => {
// Success
})
.catch((error) => {
// Error
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
// console.log(error.response.data);
// console.log(error.response.status);
// console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}
console.log(error.config);
});
Origin url https://gist.github.com/fgilio/230ccd514e9381fafa51608fcf137253
axios.post('http://localhost:8000/api/auth/register', {
username : 'test'
}).then(result => {
console.log(result.data)
}).catch(err => {
console.log(err.response.data)
})
add in catch
geting error response ==> err.response.data
I was also stumped on this for a while. I won't rehash things too much, but I thought it would be helpful to others to add my 2 cents.
The error in the code above is of type Error. What happens is the toString method is called on the error object because you are trying to print something to the console. This is implicit, a result of writing to the console. If you look at the code of toString on the error object.
Error.prototype.toString = function() {
'use strict';
var obj = Object(this);
if (obj !== this) {
throw new TypeError();
}
var name = this.name;
name = (name === undefined) ? 'Error' : String(name);
var msg = this.message;
msg = (msg === undefined) ? '' : String(msg);
if (name === '') {
return msg;
}
if (msg === '') {
return name;
}
return name + ': ' + msg;
};
So you can see above it uses the internals to build up the string to output to the console.
There are great docs on this on mozilla.
The only thing what helped me was the following:
axios.put('/api/settings', settings, {
validateStatus: status => status >= 200 && status < 300 || status === 422
})
https://stackoverflow.com/a/66285529/5849569
You can use inline if else statement like so:
.catch(error => {
dispatch({
type: authActions.AUTH_PROCESS_ERROR,
error: error.response ? error.response.data.code.toString() : 'Something went wrong, please try again.'
});
});
I recommend handling errors via Axios interceptors, individually for each case scenario:
// interceptor to catch errors
const errorInterceptor = (error) => {
// check if it's a server error
if (!error.response) {
console.log('📡 API | Network/Server error')
return Promise.reject(error)
}
// all the error responses
switch (error.response.status) {
case 400:
console.error(error.response.status, error.message)
console.log('📡 API | Nothing to display', 'Data Not Found')
break
case 401: // authentication error, logout the user
console.log('📡 API | Please login again', 'Session Expired')
localStorage.removeItem('user')
break
case 403:
console.error(error.response.status, error.message)
console.log('📡 API | Access denied', 'Data Not Found')
break
case 404:
console.error(error.response.status, error.message)
console.log('📡 API | Dataset not found', 'Data Not Found')
break
case 422:
console.error(error.response.status, error.message, error.response.data.detail)
console.log('📡 API | Validation error', 'Unprocessable Content')
break
default:
console.error(error.response.status, error.message)
}
return Promise.reject(error)
}

Categories

Resources