Angular 6 & 7 HTTP Client Interceptor с обработкой ошибок

Код

В данном руководстве объясняется, как в Angular 6 и 7 с помощью интерцепторов (перехватчиков) обрабатывать HTTP-запросы и ответы.

Ресурсы

Нам понадобиться node.js 8.12.0 и npm v6.4.1, а также Angular CLI.

$ npm install -g angular-cli

Вам также необходимо иметь общее представление о создании приложений с помощью Angular CLI.

Создание приложения

Для создания приложения в CLI выполните следующие команды:

$ ng new Angular-Interceptor 
$ cd Angular-Interceptor
$ ng serve

После этого открываем в браузере ссылку http://localhost:4200 и добавляем компонент material в Angular-приложение. Он нужен для улучшения работы пользовательского интерфейса. Подробное описание установки компонента можно найти здесь.

Шаг 1: Установка  Angular Material, Angular CDK и Angular Animations

Выполните следующие команды:

npm install --save @angular/material @angular/cdk @angular/animations

Шаг 2: Настройка анимации

Импортируйте BrowserAnimationsModule в свой AppModule

import {BrowserAnimationsModule} from '@angular/platform-browser/animations';

@NgModule({
  ...
  imports: [BrowserAnimationsModule],
  ...
})
export class AppModule { }

Шаг 3: Импортирование модулей компонентов

Импортируем MatDialogModule в модуль AppModule.

import { MatDialogModule } from '@angular/material';

@NgModule({
  imports: [MatDialogModule],
})
export class AppModule { }

 Шаг 4: Добавление темы

Добавьте эту строку в файл styles.css

@import "~@angular/material/prebuilt-themes/indigo-pink.css";

Создание перехватчика Angular

Создайте папку для перехватчика с именем вашего приложения. После этого создайте новый файл httpconfig.interceptor.ts в папке интерцептора.

Подключите следующие зависимости в файле httpconfig.interceptor.ts

import { Injectable } from '@angular/core';
import { ErrorDialogService } from '../error-dialog/errordialog.service';
import {
    HttpInterceptor,
    HttpRequest,
    HttpResponse,
    HttpHandler,
    HttpEvent,
    HttpErrorResponse
} from '@angular/common/http';

import { Observable, throwError } from 'rxjs';
import { map, catchError } from 'rxjs/operators';

В Angular 6 и 7 rxjs map изменяется на путь rxjs/operators. Будьте внимательны во время процесса импортирования.

Создаем класс HttpConfigInterceptor ** и реализуем интерфейс HttpInterceptor**

export class HttpConfigInterceptor implements HttpInterceptor

@Injectable() export class HttpConfigInterceptor implements HttpInterceptor { }

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

В данном примере мы устанавливаем токен Content-Type и Accept type в запросе API.

const token: string = localStorage.getItem('token');
request = request.clone({ headers: request.headers.set('Authorization', 'Bearer ' + token) });
request = request.clone({ headers: request.headers.set('Content-Type', 'application/json') });
request = request.clone({ headers: request.headers.set('Accept', 'application/json') });

Ниже приведен весь код:

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const token: string = localStorage.getItem('token');

        if (token) {
            request = request.clone({ headers: request.headers.set('Authorization', 'Bearer ' + token) });
        }

        if (!request.headers.has('Content-Type')) {
            request = request.clone({ headers: request.headers.set('Content-Type', 'application/json') });
        }

        request = request.clone({ headers: request.headers.set('Accept', 'application/json') });

        return next.handle(request).pipe(
            map((event: HttpEvent<any>) => {
                if (event instanceof HttpResponse) {
                    console.log('event--->>>', event);
                }
                return event;
            }));
    }

Из кода, приведенного выше, мы можем установить Content-Type, Accept type и токен в API. Он обрабатывает только API-запрос.

map((event: HttpEvent<any>) => {
                if (event instanceof HttpResponse) {
                    console.log('event--->>>', event);
                    // this.errorDialogService.openDialog(event);
                }
                return event;
            }),

Приведенный выше код будет обрабатывать ответ API. Мы можем обрабатывать каждый отклик API.

Для обработки ответа об ошибке нужно импортировать модуль rxjs

import { map, catchError } from 'rxjs/operators';

Код, приведенный ниже, будет обрабатывать ответ об ошибке:

catchError((error: HttpErrorResponse) => {
                let data = {};
                data = {
                    reason: error && error.error.reason ? error.error.reason : '',
                    status: error.status
                };
                this.errorDialogService.openDialog(data);
                return throwError(error);
            })

Здесь я добавил errorDialogService для обработки ошибок и отображения сообщения об ошибке, предназначенного для пользователей. Но сначала нужно импортировать httpconfig.interceptor.ts ** в модуль AppModule**.

import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { HttpConfigInterceptor} from './interceptor/httpconfig.interceptor';

Добавляем свой класс в провайдеры. Для обработки множественных перехватов устанавливаем multi: true

providers: [
{ provide: HTTP_INTERCEPTORS, useClass: HttpConfigInterceptor, multi: true }
]

Создание отдельной службы для обработки ошибок

Чтобы обрабатывать ответы об ошибке, создаем новый файл errordialof.sercive.ts с использованием приведенного ниже кода:

import { Injectable } from '@angular/core';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { ErrorDialogComponent } from './errordialog.component';

@Injectable()
export class ErrorDialogService {

    constructor(public dialog: MatDialog) { }
    openDialog(data): void {
        const dialogRef = this.dialog.open(ErrorDialogComponent, {
            width: '300px',
            data: data
        });

        dialogRef.afterClosed().subscribe(result => {
            console.log('The dialog was closed');
            let animal;
            animal = result;
        });
    }
}

Добавляем компонент errordialog.component для отображения диалогового окна о возникновении ошибки.

import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material';

@Component({
  selector: 'app-root',
  templateUrl: './errordialog.component.html'
})
anexport class ErrorDialogComponent {
  title = 'Angular-Interceptor';
  constructor(@Inject(MAT_DIALOG_DATA) public data: string) {}
}
<div>
    <div>
        <p>
            Reason: {{data.reason}}
        </p>
        <p>
            Status: {{data.status}}
        </p>
    </div>
</div>

Импортируем файл errordialog.service и компонент диалога ошибок в модуль приложения.

Создание примера сервисного файла для HTTP-запроса

У меня есть пример файла службы для вызова API.

LogIn API Customer detail API

I have created the service name called login.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
// import 'rxjs/operator/map';

@Injectable()
export class LoginService {

    constructor(private http: HttpClient) { }

    login(data) {
        data = { email: 'admin', password: 'admin' };
        return this.http.post('http://localhost:3070/api/login', data);
    }

    getCustomerDetails() {
        return this.http.get('http://localhost:3070/customers/details');
    }

}

Добавляем функцию для вызова двух API.

Запуск службы HTTP-клиента в компоненте приложения

Я использовал две функции function app.componet.ts: для логина API и вызова функции нажатия кнопки мышки.

import { Component } from '@angular/core';
import { LoginService } from './services/login.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'Angular-Interceptor';
  constructor(public loginService: LoginService) {
    this.loginService.login({}).subscribe(data => {
      console.log(data);
    });
  }

  getCustomerDetails() {
    this.loginService.getCustomerDetails().subscribe((data) => {
      console.log('----->>>', data);
    });
  }
}
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
  <h1>
    Welcome to {{ title }}!
  </h1>
  <img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
</div>
<h2>Here are some links to help you start: </h2>
<ul>
  <li>
    <h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
  </li>
  <li>
    <h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
  </li>
  <li>
    <h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
  </li>
  <li>
    <h2><p (click)="getCustomerDetails()">Get customer details</p></h2>
  </li>
</ul>

<router-outlet></router-outlet>

Вот так выглядит диалоговое окно обработчика ошибок:

Запуск службы HTTP-клиента в компоненте приложения

Пример кода

Я разместил полный код примера на github. Вы можете запустить npm и сервер ng для тестирования кода. Не забудьте изменить адрес API URL серверной части в файле входа в систему.

Заключение

Мы рассмотрели, как обрабатывать http-запрос и ответ на него с использованием перехватчиков в Angular 6 и 7 interceptor.

Наталья Кайдаавтор-переводчик статьи «Angular 6 & 7 HTTP Client Interceptor with Error handling.»

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