Angular — выход из системы при 401 неавторизованном или 403 запрещенном HTTP-ответе с перехватчиком
Это быстрый пример того, как автоматически выйти из приложения Angular, если HTTP-запрос возвращает ответ 401 Unauthorized
или 403 Forbidden
.
Приведенные ниже фрагменты кода взяты из руководства по входу в Angular, которое автоматически выходит из системы, если из API возвращается ответ об ошибке 401 или 403. Полное руководство, включая живую демонстрацию кода, см. в разделе Angular 14 — Пример и руководство по регистрации и входу в систему.
Перехватчик ошибок HTTP с выходом из системы на 401 или 403
Путь: /src/app/_helpers/error.interceptor.ts
Перехватчик ошибок перехватывает все ответы HTTP от API, чтобы проверить наличие ошибок. Если есть ответ 401 Unauthorized
или 403 Forbidden
, пользователь автоматически выходит из приложения, все другие ошибки повторно выдаются вызывающей службе или компоненту.
Он реализован с использованием интерфейса Angular HttpInterceptor
, включенного в HttpClientModule
. Благодаря реализации интерфейса HttpInterceptor вы можете создать собственный перехватчик для перехвата всех ответов об ошибках от API в одном месте.
Перехватчики HTTP добавляются в конвейер запросов в разделе providers
файла app.module.ts.
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { AccountService } from '@app/_services';
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
constructor(private accountService: AccountService) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(catchError(err => {
if ([401, 403].includes(err.status) && this.accountService.userValue) {
// auto logout if 401 or 403 response returned from api
this.accountService.logout();
}
const error = err.error?.message || err.statusText;
console.error(err);
return throwError(() => error);
}))
}
}
Служба учетных записей, которая отправляет HTTP-запросы
Path: /src/app/_services/account.service.ts
Служба учетных записей обрабатывает связь между приложением Angular и внутренним API для всего, что связано с учетными записями. Он содержит методы для входа, выхода и регистрации, а также стандартные методы CRUD для получения, изменения и удаления пользовательских данных.
Включим его сюда, чтобы показать примеры HTTP-запросов, отправляемых из Angular в API.
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { environment } from '@environments/environment';
import { User } from '@app/_models';
@Injectable({ providedIn: 'root' })
export class AccountService {
private userSubject: BehaviorSubject<User | null>;
public user: Observable<User | null>;
constructor(
private router: Router,
private http: HttpClient
) {
this.userSubject = new BehaviorSubject(JSON.parse(localStorage.getItem('user')!));
this.user = this.userSubject.asObservable();
}
public get userValue() {
return this.userSubject.value;
}
login(username: string, password: string) {
return this.http.post<User>(`${environment.apiUrl}/users/authenticate`, { username, password })
.pipe(map(user => {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('user', JSON.stringify(user));
this.userSubject.next(user);
return user;
}));
}
logout() {
// remove user from local storage and set current user to null
localStorage.removeItem('user');
this.userSubject.next(null);
this.router.navigate(['/account/login']);
}
register(user: User) {
return this.http.post(`${environment.apiUrl}/users/register`, user);
}
getAll() {
return this.http.get<User[]>(`${environment.apiUrl}/users`);
}
getById(id: string) {
return this.http.get<User>(`${environment.apiUrl}/users/${id}`);
}
update(id: string, params: any) {
return this.http.put(`${environment.apiUrl}/users/${id}`, params)
.pipe(map(x => {
// update stored user if the logged in user updated their own record
if (id == this.userValue?.id) {
// update local storage
const user = { ...this.userValue, ...params };
localStorage.setItem('user', JSON.stringify(user));
// publish updated user to subscribers
this.userSubject.next(user);
}
return x;
}));
}
delete(id: string) {
return this.http.delete(`${environment.apiUrl}/users/${id}`)
.pipe(map(x => {
// auto logout if the logged in user deleted their own record
if (id == this.userValue?.id) {
this.logout();
}
return x;
}));
}
}
Модуль приложения, добавляющий HTTP-перехватчик
Путь: /src/app/app.module.ts
Модуль приложения определяет корневой модуль приложения Angular вместе с метаданными о модуле.
ErrorInterceptor
настраивается как перехватчик HTTP для приложения в разделе providers
со следующими свойствами:
provide: HTTP_INTERCEPTORS
- токен внедрения Angular, используемый поставщиком DI, который сопоставляется с массивом перехватчиков HTTP для приложения.useClass: ErrorInterceptor
- класс для добавления в массив HTTP_INTERCEPTORS.multi: true
- требуется для HTTP_INTERCEPTORS, он настраивает токен внедрения как токен с несколькими поставщиками, представляющий массив значений.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
// used to create fake backend
import { fakeBackendProvider } from './_helpers';
import { AppRoutingModule } from './app-routing.module';
import { JwtInterceptor, ErrorInterceptor } from './_helpers';
import { AppComponent } from './app.component';
import { AlertComponent } from './_components';
import { HomeComponent } from './home';
@NgModule({
imports: [
BrowserModule,
ReactiveFormsModule,
HttpClientModule,
AppRoutingModule
],
declarations: [
AppComponent,
AlertComponent,
HomeComponent
],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
// provider used to create fake backend
fakeBackendProvider
],
bootstrap: [AppComponent]
})
export class AppModule { };