Angular обработка ошибок http

Обработка ошибок¶

При работе с сетью и http нередко могут происходить ошибки, например, в момент запроса сеть стала недоступна, неправильно указан адрес и соответственно не найден ресурс, либо доступ к ресурсу ограничен, либо другие ошибки. Перехват ошибок позволит выяснить проблему и каким-то образом обработать их, например, вывести пользователю сообщение об ошибке.

Например, изменим код компонента AppComponent:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import { Component, OnInit } from '@angular/core';
import { HttpService } from './http.service';
import { User } from './user';

@Component({
    selector: 'my-app',
    template: `
        <div>{{ error }}</div>
        <ul>
            <li *ngFor="let user of users">
                <p>Имя пользователя: {{ user?.name }}</p>
                <p>Возраст пользователя: {{ user?.age }}</p>
            </li>
        </ul>
    `,
    providers: [HttpService],
})
export class AppComponent implements OnInit {
    users: User[] = [];
    error: any;
    constructor(private httpService: HttpService) {}

    ngOnInit() {
        this.httpService.getUsers().subscribe(
            (data) => (this.users = data),
            (error) => {
                this.error = error.message;
                console.log(error);
            }
        );
    }
}

Метод subscribe() в качестве второго параметра принимает обработчик, который вызывается в случае возникновения ошибки. В этом обработчике мы можем получить ошибку и обработать ее. Ошибка собственно представляет объект, из которого мы можем получить ряд данных. В частности, свойство message позволяет получить сообщение об ошибке, а свойство statusCode — статусный код ответа. И в данном случае в компоненте определена переменная для хранения сообщения об ошибке. И в шаблоне эта переменная выводится на веб-страницу. И например, при обащении к несуществующему файлу json мы получим следующую ошибку:

Скриншот приложения

Кроме того, для перехвата ошибок к объекту Observable, который является результатом запроса, можно использовать функцию catchError(). Так, возьмем код сервиса из прошлой темы и добавим к нему обработку ошибок:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { User } from './user';
import { Observable, throwError } from 'rxjs';
import { map, catchError } from 'rxjs/operators';

@Injectable()
export class HttpService {
    constructor(private http: HttpClient) {}

    getUsers(): Observable<User[]> {
        return this.http.get('usersP.json').pipe(
            map((data) => {
                let usersList = data['usersList'];
                return usersList.map(function (user: any) {
                    return {
                        name: user.userName,
                        age: user.userAge,
                    };
                });
            }),
            catchError((err) => {
                console.log(err);
                return throwError(err);
            })
        );
    }
}

Для имитации исключения здесь передается заведомо несуществующий адрес usersP.json. Для обработки ошибок в метод pipe() передается в качестве второго параметра функция для обработки ошибок. В качестве подобной функции используется функция catchError(). Она принимает объект ошибки, который затем выводится на консоль. С помощью вывода на консоль мы можем исследовать объект. Это тот же объект, который мы выше получаем в AppComponent в методе subscribe. С помощью метода throwError() возвращается результат — новый объект Observable, который содержит информацию об ошибке.

You have some options, depending on your needs. If you want to handle errors on a per-request basis, add a catch to your request. If you want to add a global solution, use HttpInterceptor.

Open here the working demo plunker for the solutions below.

tl;dr

In the simplest case, you’ll just need to add a .catch() or a .subscribe(), like:

import 'rxjs/add/operator/catch'; // don't forget this, or you'll get a runtime error
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);
      });

// or
this.httpClient
      .get("data-url")
      .subscribe(
        data => console.log('success', data),
        error => console.log('oops', error)
      );

But there are more details to this, see below.

Method (local) solution: log error and return fallback response

If you need to handle errors in only one place, you can use catch and return a default value (or empty response) instead of failing completely. You also don’t need the .map just to cast, you can use a generic function. Source: Angular.io — Getting Error Details.

So, a generic .get() method, would be like:

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from "@angular/common/http";
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/empty';
import 'rxjs/add/operator/retry'; // don't forget the imports

@Injectable()
export class DataService {
    baseUrl = 'http://localhost';
    constructor(private httpClient: HttpClient) { }

    // notice the <T>, making the method generic
    get<T>(url, params): Observable<T> {
      return this.httpClient
          .get<T>(this.baseUrl + url, {params})
          .retry(3) // optionally add the retry
          .catch((err: HttpErrorResponse) => {

            if (err.error instanceof Error) {
              // A client-side or network error occurred. Handle it accordingly.
              console.error('An error occurred:', err.error.message);
            } else {
              // The backend returned an unsuccessful response code.
              // The response body may contain clues as to what went wrong,
              console.error(`Backend returned code ${err.status}, body was: ${err.error}`);
            }

            // ...optionally return a default fallback value so app can continue (pick one)
            // which could be a default value
            // return Observable.of<any>({my: "default value..."});
            // or simply an empty observable
            return Observable.empty<T>();
          });
     }
}

Handling the error will allow you app to continue even when the service at the URL is in bad condition.

This per-request solution is good mostly when you want to return a specific default response to each method. But if you only care about error displaying (or have a global default response), the better solution is to use an interceptor, as described below.

Run the working demo plunker here.

Advanced usage: Intercepting all requests or responses

Once again, Angular.io guide shows:

A major feature of @angular/common/http is interception, the ability to declare interceptors which sit in between your application and the backend. When your application makes a request, interceptors transform it before sending it to the server, and the interceptors can transform the response on its way back before your application sees it. This is useful for everything from authentication to logging.

Which, of course, can be used to handle errors in a very simple way (demo plunker here):

import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse,
         HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/empty';
import 'rxjs/add/operator/retry'; // don't forget the imports

@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request)
      .catch((err: HttpErrorResponse) => {

        if (err.error instanceof Error) {
          // A client-side or network error occurred. Handle it accordingly.
          console.error('An error occurred:', err.error.message);
        } else {
          // The backend returned an unsuccessful response code.
          // The response body may contain clues as to what went wrong,
          console.error(`Backend returned code ${err.status}, body was: ${err.error}`);
        }

        // ...optionally return a default fallback value so app can continue (pick one)
        // which could be a default value (which has to be a HttpResponse here)
        // return Observable.of(new HttpResponse({body: [{name: "Default value..."}]}));
        // or simply an empty observable
        return Observable.empty<HttpEvent<any>>();
      });
  }
}

Providing your interceptor: Simply declaring the HttpErrorInterceptor above doesn’t cause your app to use it. You need to wire it up in your app module by providing it as an interceptor, as follows:

import { NgModule } from '@angular/core';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { HttpErrorInterceptor } from './path/http-error.interceptor';

@NgModule({
  ...
  providers: [{
    provide: HTTP_INTERCEPTORS,
    useClass: HttpErrorInterceptor,
    multi: true,
  }],
  ...
})
export class AppModule {}

Note: If you have both an error interceptor and some local error handling, naturally, it is likely that no local error handling will ever be triggered, since the error will always be handled by the interceptor before it reaches the local error handling.

Run the working demo plunker here.

HTTP errors are common in Angular while making an HTTP request, it’s a good practice to handle these errors without hampering the user’s experience. In this article, we’ll discuss three ways of dealing with such cases.

First things first, let’s understand the error interceptor.

What is the Error Interceptor?

An error interceptor is a special type of interceptor which is used for handling errors that arise while making an HTTP request. The error may come in either the client-side (browser) or it may be an error from the server-side when the request fails due to any reason. You can learn more about the internal implementation of interceptors in Insider’s guide into interceptors and HttpClient mechanics in Angular.

Let’s see the error interceptor in practice.

Project Setup

First of all, let’s create a new project using the ng new command (I named the project http-interceptor).
After creating the project successfully, let’s create the needed components & services. Run the following commands:

  • ng g c components/demo-component (to create a new component named demo)

  • ng g s services/api (to create an api service)

  • ng g interceptor interceptors/error-catching (to create an error-catching interceptor)
    After running the above commands you should get a project structure like this.

    22

Clean the app.component.html and add the <app-demo-component></app-demo-component> tag in the demo-component.component.html add this piece of code:

<>Copy

<button (click)="getData()">Get Data</button> <div *ngIf="data">{{data}}</div>

Simple enough right! Nothing fancy happening here. Before starting with the fun part, let’s create a function to send an HTTP request.

<>Copy

// demo-component.component.ts import {Component, OnInit} from '@angular/core'; import {ApiService} from "../../services/api.service"; @Component({ selector: 'app-demo-component', templateUrl: './demo-component.component.html', styleUrls: ['./demo-component.component.scss'] }) export class DemoComponentComponent implements OnInit { data = "" constructor(private apiService: ApiService) { } ngOnInit(): void { } getData() { this.apiService.getData().subscribe(res => { this.data = JSON.stringify(res) }) } }

<>Copy

// api.service.ts import {Injectable} from '@angular/core'; import {HttpClient} from "@angular/common/http"; import {Observable} from "rxjs"; @Injectable({ providedIn: 'root' }) export class ApiService { url = 'https://jsonplaceholder.typicode.com/todos/10'; constructor(private http: HttpClient) { } getData(): Observable<any> { return this.http.get(this.url); } }

At this point, it’s a typical HTTP request. Now, let’s start with the fun part.

Use HTTP Interceptor

Now, let’s dive deep into the interesting part. First, let’s take a look at the error-catching.interceptor.ts file.

Notice that it implements the HttpInterceptor interface, which forces us to implement the intercept function which identifies and handles a given HTTP request.

What about the intercept function parameters?

  • req – The outgoing request object to handle.
  • next – The next interceptor in the chain, or the backend if no interceptors remain in the chain.
  • Returns: an observable of the event stream.

Now let’s add our touch. Take a look at the next code:

<>Copy

import {Injectable} from '@angular/core'; import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http'; import {Observable, throwError} from 'rxjs'; import {catchError} from "rxjs/operators"; @Injectable() export class ErrorCatchingInterceptor implements HttpInterceptor { constructor() { } intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> { return next.handle(request) .pipe( catchError((error: HttpErrorResponse) => { let errorMsg = ''; if (error.error instanceof ErrorEvent) { console.log('This is client side error'); errorMsg = `Error: ${error.error.message}`; } else { console.log('This is server side error'); errorMsg = `Error Code: ${error.status}, Message: ${error.message}`; } console.log(errorMsg); return throwError(errorMsg); }) ) } }

What is happening here? I’ll explain.

Inside the pipe() operator of rxjs we are catching any error and checking for the source of it (whether it’s from the client-side or from the server-side). Then we are returning the error through the throwError() operator of rxjs.

Now, let’s use the interceptor file in our project. We will add it to the providers array in the app.module.ts.

<>Copy

@NgModule({ ... providers: [ { provide: HTTP_INTERCEPTORS, useClass: ErrorCatchingInterceptor, multi: true } ], ... }) export class AppModule { }

Let’s make sure that everything is working as expected.

What is the expected behavior?

The expected behavior is every request/response should pass through the interceptor.

For the sake of the demo, we will modify the error-catching.interceptor.ts file, and add two console.log()

<>Copy

import {Injectable} from '@angular/core'; import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http'; import {Observable, throwError} from 'rxjs'; import {catchError, map} from "rxjs/operators"; @Injectable() export class ErrorCatchingInterceptor implements HttpInterceptor { constructor() { } intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> { console.log("Passed through the interceptor in request"); return next.handle(request) .pipe( map(res => { console.log("Passed through the interceptor in response"); return res }), catchError((error: HttpErrorResponse) => { let errorMsg = ''; if (error.error instanceof ErrorEvent) { console.log('This is client side error'); errorMsg = `Error: ${error.error.message}`; } else { console.log('This is server side error'); errorMsg = `Error Code: ${error.status}, Message: ${error.message}`; } console.log(errorMsg); return throwError(errorMsg); }) ) } }

You should see the following output:

33

Now, let’s break the URL to make the request fail.

<>Copy

url = 'https://jsonplaceholder.typicode.com/tod';

Now, after running the code with a broken URL you should see the following output

44

Conclusion

HttpInterceptors is a great tool for modern applications, it has great benefits. Following are a few of them:

  • Code reusability — since we are expected to perform a certain common set of steps against a majority of the HTTP requests and responses, we can write the logic in one place and enable the request and response flow to use that logic. This way, instead of replicating the same logic individually on a service level, we write that once and hence it produces a lot less redundant code which is also a clean practice to follow.
  • Scalability — this point is, kind of, linked to the first point in a way that it provides an extension to the idea stated earlier. If the code is reusable then it automatically makes sense that it will be a lot easier to scale the code and its design.
  • Better service layer — since we can handle the related activities using interceptors, we do not need to pollute the service layer with handling that other stuff. This way, the service layer will be clean, to the point, and will only do what it is responsible for doing.

References

How to split http interceptors between multiple backends

Insiders guide into interceptors and httpclient mechanics in angular

Top 10 ways to use interceptors in angular

Http intercepting requests-and responses

How to implement automatic token insertion in requests using HTTP interceptor

Обработка ошибок

Последнее обновление: 04.05.2023

При работе с сетью и http нередко могут происходить ошибки, например, в момент запроса сеть стала недоступна, неправильно указан адрес и соответственно не найден
ресурс, либо доступ к ресурсу ограничен, либо другие ошибки. Перехват ошибок позволит выяснить проблему и каким-то образом обработать их, например,
вывести пользователю сообщение об ошибке.

Для перехвата ошибок, которые могут возникнуть при выполнении запроса, можно использовать функцию catchError(). Так,
возьмем код сервиса из прошлой темы и добавим к нему обработку ошибок:

import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {User} from './user';
import {Observable, throwError} from 'rxjs';
import { map, catchError} from 'rxjs/operators';
 
@Injectable()
export class HttpService{
    
    errorMessage: String = "";
    constructor(private http: HttpClient){ }
        
    getUsers() : Observable<User[]> {
        return this.http.get('assets/usersP.json').pipe(map((data:any)=>{
            let usersList = data["userList"];
            
            return usersList.map(function(user:any) : User {
                return new User(user.userName, user.userAge);
              });
        }),
        catchError(err => {  
            console.log(err); 
            this.errorMessage = err.message;
            return [];
        }))
    };
}

Прежде всего в сервисе определяется переменная errorMessage, которая будет хранит информацию об ошибке.

Для имитации ошибки в http-клиент передается заведомо несуществующий адрес «usersP.json». Для обработки ошибок в метод
pipe() передается в качестве второго параметра функция для обработки ошибок. В качестве подобной функции здесь применяется функция catchError():

catchError(err => {  
            console.log(err); 
            this.errorMessage = err.message;
            return [];
        })

В качестве параметра функция catchError() принимает функцию, в которую в качестве параметра передается объект ошибки, возникшей
при выполнении запроса. Таким образом, в этой функции мы можем получить ошибку и обработать ее.

Ошибка собственно представляет объект, из которого мы можем получить ряд данных. В частности,
свойство message позволяет получить сообщение об ошибке, а свойство status — статусный код ответа.

Так, в данном случае вся обработка заключается в том, что этот объект выводится на консоль, а свойству errorMessage сервиса
передается сообщение об ошибке (если запрос прошел успешно, то этому свойству присваивается пустая строка).

Стоит отметить, что в функции обработки ошибки нам все равно надо вернуть объект Observable. Для этого мы возвращаем пустой массив:

return [];

Далее будет создан объект Observable>User[]<, который будет содержать пустой массив объектов User.

Например, используем сервис и для этого изменим код компонента AppComponent:

import { Component, OnInit} from '@angular/core';
import { HttpService} from './http.service';
import {User} from './user';
   
@Component({
    selector: 'my-app',
    template: `<div>{{this.httpService.errorMessage}}</div>
                <ul>
                <li *ngFor="let user of users">
                <p>Имя пользователя: {{user?.name}}</p>
                <p>Возраст пользователя: {{user?.age}}</p>
                </li>
            </ul>`,
    providers: [HttpService]
})
export class AppComponent implements OnInit { 
   
    users: User[]=[];
    error:any;
    constructor(public httpService: HttpService){}
      
    ngOnInit(){
          
        this.httpService.getUsers().subscribe({next: data=>this.users=data});
    }
}

С помощью метода subscribe() компонент может получить из сервиса массив объектов User. Если возникнет ошибка, то это будет пустой массив.

Для получения информации об ошибке компонент обращается к свойству errorMessage сервиса и выводит его значение в размеке html.

И например, при обащении к несуществующему файлу json мы получим следующую ошибку:

Передача ошибки в компонент в Angular 16

Это самая примитивная обработка ошибка, которая демонстрирует общий принцип. Естественно в сервисе мы можем определять какое-то другое сообщение об ошибке или как-то иначе обрабатывать ошибку.

In this guide, we learn about Angular HTTP Error Handling. Whenever the error occurs in an HTTP operation, the Angular wraps it in an httpErrorResponse Object before throwing it back. We catch the httpErrorResponse either in our component class or in the data service class or globally. The Global HTTP error handling is done using the Angular HTTP Interceptor.

Suggested Reading: Error Handling in Angular

Table of Contents

  • HttpErrorResponse
  • Catching Errors in HTTP Request
    • Catch Errors in Component
    • Catch Errors in Service
    • Catch error globally using HTTP Interceptor
  • HTTP Error Handling
  • HTTP Error handling example
  • References
  • Summary

HttpErrorResponse

The HttpClient captures the errors and wraps it in the generic HttpErrorResponse, before passing it to our app. The error property of the HttpErrorResponse contains the underlying error object. It also provides additional context about the state of the HTTP layer when the error occurred.

The HTTP errors fall into two categories. The back end server may generate the error and send the error response. Or the client-side code may fail to generate the request and throw the error (ErrorEvent objects).

The server might reject the request for various reasons. Whenever it does it will return the error response with the HTTP Status Codes such as Unauthorized (401), Forbidden (403), Not found (404), internal Server Error (500), etc. The Angular assigns the error response to error property of the HttpErrorResponse.

The client-side code can also generate the error. The error may be due to a network error or an error while executing the HTTP request or an exception thrown in an RxJS operator. These errors produce JavaScript ErrorEvent objects. The Angular assigns the ErrorEvent object to error property of the HttpErrorResponse.

In both the cases, the generic HttpErrorResponse is returned by the HTTP Module. We will inspect the error property to find out the type of Error and handle accordingly.

Catching Errors in HTTP Request

We can catch the HTTP Errors at three different places.

  1. Component
  2. Service
  3. Globally

Catch Errors in Component

Refer to our tutorial on Angular HTTP Get Request. We created a GitHubService, where we made a GET request to the GitHub API to get the list of Repositories. The following is the getRepos() method from the service. We have intentionally changed the URL (uersY) so that it will result in an error.

getRepos(userName: string): Observable<any> {

   return this.http.get(this.baseURL + ‘usersY/’ + userName + ‘/repos’)

}

We subscribe to the httpClient.get method in the component class

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

  public getRepos() {

    this.loading = true;

    this.errorMessage = «»;

    this.githubService.getReposCatchError(this.userName)

      .subscribe(

        (response) => {                           //Next callback

          console.log(‘response received’)

          this.repos = response;

        },

        (error) => {                              //Error callback

          console.error(‘error caught in component’)

          this.errorMessage = error;

          this.loading = false;

          //throw error;   //You can also throw the error to a global error handler

        }

      )

  }

The subscribe method has three callback arguments.

.subscribe(success, error, completed);

The observable invokes the first callback success, when the HTTP request successfully returns a response. The third call back completed is called when the observable finishes without any error.

The second callback error, is invoked when the HTTP Request end in an error. We handle error here by figuring out the type of error and handle it accordingly. It gets the error object which is of type HttpErrorResponse.

        (error) => {                              //Error callback

          console.error(‘error caught in component’)

          this.errorMessage = error;

          this.loading = false;

        }

Catch Errors in Service

We can also catch errors in the service, which makes the HTTP Request using the catchError Operator as shown below. Once you handle the error, you can re-throw it back to the component for further handling.

  getRepos(userName: string): Observable<repos[]> {

    return this.http.get<repos[]>(this.baseURL + ‘usersY/’ + userName + ‘/repos’)

      .pipe(

        catchError((err) => {

          console.log(‘error caught in service’)

          console.error(err);

          //Handle the error here

          return throwError(err);    //Rethrow it back to component

        })

      )

  }

Catch error globally using HTTP Interceptor

The type of error we may encounter vary. But some of those errors are common to every HTTP request. For Example

  1. You are unauthorized to access the API Service,
  2. You are authorized, but forbidden to access a particular resource
  3. The API End Point is invalid or does not exist
  4. Network error
  5. Server down

We can check all these errors in the service or in component, but our app may contain many such service or components. Checking for common errors in each and every method is inefficient and error-prone.

The Right thing to do is to handle only the errors specific to this API call in this component/service and move all the common errors to one single place. This is where we use the HTTP Interceptor.

The HTTP Interceptor is a service, which we create and register it globally at the root module using the Angular Providers. Once defined, it will intercept all the HTTP requests passing through the app. It intercepts when we make the HTTP request and also intercepts when the response arrives. This makes it an ideal place to catch all the common errors and handle it

We create the Interceptor by creating a Global Service class, which implements the HttpInterceptor Interface. Then we will override the intercept method in that service.

The following code shows a simple GlobalHttpInterceptorService

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

import {Injectable} from «@angular/core»;

import {HttpEvent, HttpHandler, HttpInterceptor,HttpRequest,HttpResponse,HttpErrorResponse} from ‘@angular/common/http’;

import {Observable, of, throwError} from «rxjs»;

import {catchError, map} from ‘rxjs/operators’;

@Injectable()

export class GlobalHttpInterceptorService implements HttpInterceptor {

  constructor(public router: Router) {

  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    return next.handle(req).pipe(

      catchError((error) => {

        console.log(‘error is intercept’)

        console.error(error);

        return throwError(error.message);

      })

    )

  }

}

The caching of the Error is done using the catchError RxJS operator. We then re-throw it to the subscriber using the throwError

The catchError is added to the request pipeline using the RxJs pipe operator . When the error occurs in the HTTP Request it is intercepted and invokes the catchError. Inside the catchError you can handle the error and then use throwError to throw it to the service.

We then register the Interceptor in the Providers array of the root module using the injection token HTTP_INTERCEPTORS. Note that you can provide more than one Interceptor (multi: true).

providers: [

    GitHubService,

    { provide: HTTP_INTERCEPTORS, useClass: GlobalHttpInterceptorService, multi: true  }

]

Next, step is what to do with the errors

The server-side errors return status codes, we can take appropriate actions based on that. For Example for Status code 401 Unauthorized, we can redirect the user to the login page, for 408 Request Timeout, we can retry the operation, etc.

The following example code shows how to check for status codes 401 & 403 and redirect to the login page.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

if (error instanceof HttpErrorResponse) {

    if (error.error instanceof ErrorEvent) {

        console.error(«Error Event»);

    } else {

        console.log(`error status : ${error.status} ${error.statusText}`);

        switch (error.status) {

            case 401:      //login

                this.router.navigateByUrl(«/login»);

                break;

            case 403:     //forbidden

                this.router.navigateByUrl(«/unauthorized»);

                break;

        }

    }

} else {

    console.error(«some thing else happened»);

}

return throwError(error);

For Server errors with status codes 5XX, you can simply ask the user to retry the operation. You can do this by showing an alert box or redirect him to a special page or show the error message at the top of the page bypassing the error message to a special service AlertService.

For other errors, you can simply re-throw it back to the service.

return throwError(error);

You can further handle the error in service or throw it back to the component.

The component must display the error message to the user. You can also throw it back to a global error handler in Angular.

.subscribe(

   (response) => {

      this.repos = response;

   },

   (error) => {

      //Handle the error here

      //If not handled, then throw it

      throw error;

   }

)

HTTP Error handling example

The complete code of this example

app.component.html

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

<h1 class=«heading»><strong>Angular HTTP</strong>Error Example</h1>

<div class=«form-group»>

  <label for=«userName»>GitHub User Name</label>

  <input type=«text» class=«form-control» name=«userName» [(ngModel)]=«userName»>

</div>

<div class=«form-group»>

  <button type=«button» (click)=«getRepos()»>Get Repos</button>

</div>

<div *ngIf=«loading»>loading...</div>

<div *ngIf=«errorMessage» class=«alert alert-warning»>

  <strong>Warning!</strong> {{errorMessage | json}}

</div>

<table class=‘table’>

  <thead>

    <tr>

      <th>ID</th>

      <th>Name</th>

      <th>HTML Url</th>

      <th>description</th>

    </tr>

  </thead>

  <tbody>

    <tr *ngFor=«let repo of repos;»>

      <td>{{repo.id}}</td>

      <td>{{repo.name}}</td>

      <td>{{repo.html_url}}</td>

      <td>{{repo.description}}</td>

    </tr>

  </tbody>

</table>

<pre>{{repos | json}}</pre>

app.component.ts

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

import { Component } from ‘@angular/core’;

import { GitHubService } from ‘./github.service’;

import { repos } from ‘./repos’;

@Component({

  selector: ‘app-root’,

  templateUrl: ‘./app.component.html’,

})

export class AppComponent {

  userName: string = «tektutorialshub»

  repos: repos[];

  loading: boolean = false;

  errorMessage;

  constructor(private githubService: GitHubService) {

  }

  public getRepos() {

    this.loading = true;

    this.errorMessage = «»;

    this.githubService.getReposCatchError(this.userName)

      .subscribe(

        (response) => {                           //Next callback

          console.log(‘response received’)

          this.repos = response;

        },

        (error) => {                              //Error callback

          console.error(‘error caught in component’)

          this.errorMessage = error;

          this.loading = false;

          throw error;

        }

      )

  }

}

github.service.ts

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

import { Injectable } from ‘@angular/core’;

import { HttpClient, HttpParams, HttpHeaders } from ‘@angular/common/http’;

import { Observable, throwError } from ‘rxjs’;

import { map, catchError } from ‘rxjs/operators’;

import { repos } from ‘./repos’;

@Injectable( {providedIn:‘root’})

export class GitHubService {

  baseURL: string = «https://api.github.com/»;

  constructor(private http: HttpClient) {

  }

  //Any Data Type

  getRepos(userName: string): Observable<any> {

    return this.http.get(this.baseURL + ‘usersY/’ + userName + ‘/repos’)

  }

  //With catchError

  getReposCatchError(userName: string): Observable<repos[]> {

    return this.http.get<repos[]>(this.baseURL + ‘usersY/’ + userName + ‘/repos’)

      .pipe(

        catchError((err) => {

          console.log(‘error caught in service’)

          console.error(err);

          return throwError(err);

        })

      )

  }

}

global-http-Interceptor.service.ts

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

import { Injectable } from «@angular/core»;

import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpErrorResponse } from ‘@angular/common/http’;

import { Observable, of, throwError } from «rxjs»;

import { catchError, map } from ‘rxjs/operators’;

import { Router } from ‘@angular/router’;

@Injectable()

export class GlobalHttpInterceptorService implements HttpInterceptor {

  constructor(public router: Router) {

  }

  //1.  No Errors

  intercept1(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    return next.handle(req).pipe(

      catchError((error) => {

        console.log(‘error in intercept’)

        console.error(error);

        return throwError(error.message);

      })

    )

  }

  //2. Sending an Invalid Token will generate error

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    const token: string = ‘invald token’;

    req = req.clone({ headers: req.headers.set(‘Authorization’, ‘Bearer ‘ + token) });

    return next.handle(req).pipe(

      catchError((error) => {

        console.log(‘error in intercept’)

        console.error(error);

        return throwError(error.message);

      })

    )

  }

  intercept3(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    const token: string = ‘invald token’;

    req = req.clone({ headers: req.headers.set(‘Authorization’, ‘Bearer ‘ + token) });

    return next.handle(req).pipe(

      catchError((error) => {

        let handled: boolean = false;

        console.error(error);

        if (error instanceof HttpErrorResponse) {

          if (error.error instanceof ErrorEvent) {

            console.error(«Error Event»);

          } else {

            console.log(`error status : ${error.status} ${error.statusText}`);

            switch (error.status) {

              case 401:      //login

                this.router.navigateByUrl(«/login»);

                console.log(`redirect to login`);

                handled = true;

                break;

              case 403:     //forbidden

                this.router.navigateByUrl(«/login»);

                console.log(`redirect to login`);

                handled = true;

                break;

            }

          }

        }

        else {

          console.error(«Other Errors»);

        }

        if (handled) {

          console.log(‘return back ‘);

          return of(error);

        } else {

          console.log(‘throw error back to to the subscriber’);

          return throwError(error);

        }

      })

    )

  }

}

global-error-handler.service.ts

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

import { ErrorHandler, Injectable, Injector } from ‘@angular/core’;

import { HttpErrorResponse } from ‘@angular/common/http’;

import { throwError } from ‘rxjs’;

@Injectable()

export class GlobalErrorHandlerService implements ErrorHandler {

  constructor() {

  }

  handleError(error: Error | HttpErrorResponse) {

    console.log(‘GlobalErrorHandlerService’)

    console.error(error);

  }

}

app.module.ts

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

import { BrowserModule } from ‘@angular/platform-browser’;

import { NgModule ,ErrorHandler } from ‘@angular/core’;

import { HttpClientModule,HTTP_INTERCEPTORS} from ‘@angular/common/http’;

import { FormsModule } from ‘@angular/forms’;

import { AppComponent } from ‘./app.component’;

import { GlobalHttpInterceptorService} from ‘./global-http-Interceptor.service’;

import { AppRoutingModule } from ‘./app-routing.module’;

import { GlobalErrorHandlerService } from ‘./global-error-handler.service’;

@NgModule({

  declarations: [

    AppComponent

  ],

  imports: [

    BrowserModule,

    HttpClientModule,

    FormsModule,

    AppRoutingModule

  ],

  providers: [

    { provide: HTTP_INTERCEPTORS,    useClass: GlobalHttpInterceptorService,    multi: true  },

    { provide: ErrorHandler, useClass:GlobalErrorHandlerService}

],

  bootstrap: [AppComponent]

})

export class AppModule { }

app-routing.module.ts

import { NgModule } from ‘@angular/core’;

import { Routes, RouterModule } from ‘@angular/router’;

const routes: Routes = [];

@NgModule({

  imports: [RouterModule.forRoot(routes)],

  exports: [RouterModule]

})

export class AppRoutingModule { }

Angular HTTP Error Handling Example

References

HttpErrorResponse

Summary

Using HTTP Interceptors you can catch HTTP Errors and handle it appropriately. Check the HTTP status codes and take appropriate actions like redirecting to the login page, or redirecting to an error page or else throw the error back to the subscriber for further handling of the error.

Понравилась статья? Поделить с друзьями:
  • Aomei backupper ошибка 4141
  • Android проверка внутренней памяти на ошибки
  • Anynet overload ошибка anydesk
  • Andy ошибка 1603
  • Anylogic ошибка при выполнении дискретного события nullpointerexception