آشنایی با Interceptor در انگولار — از صفر تا صد
رهگیر یا Interceptor در انگولار چنان که از نامش مشخص است، روشی ساده برای رهگیری و ویرایش یک درخواست http پیش از ارسال به سرور، به صورت سراسری است که از سوی فریمورک ارائه شده است. این امکان در موارد مختلفی بسیار مفید است و به ما امکان میدهد که توکنهای احراز هویت را پیکربندی کنیم، لاگهای درخواست را اضافه کنیم، هدرهای سفارشی که ممکن است اپلیکیشن نیاز داشته باشد را بیفزاییم و موارد مشابه زیاد دیگری را از این دست انجام دهیم.
Interceptor-ها میتوانند وظایف بسیار مختلفی اجرا کنند و از احراز هویت تا لاگ کردن، در یک روش روتین و استاندارد برای هر درخواست/پاسخ http را شامل میشوند. توسعهدهندگان در صورت عدم استفاده از Interceptor-ها باید این وظایف را به طور صریح برای هر فراوانی متد HttpClient پیادهسازی کنند.
پیادهسازی یک Interceptor
برای پیادهسازی یک Interceptor باید یک کلاس ایجاد کنیم که یک متد Intercept را از اینترفیس HttpInterceptor پیادهسازی میکند.
فرض کنید میخواهیم هر درخواست http ارائه شده از سوی اپلیکیشن را در کنسول لاگ کنیم. در ادامه یک Interceptor ساده ساختهایم که این کار را انجام میدهد:
1@Injectable()
2export class RequestLogInterceptor implements HttpInterceptor {
3 intercept(
4 request: HttpRequest<any>, next: HttpHandler
5 ) : Observable<HttpEvent<any>> {
6 console.log(request.url);
7 return next.handle(request);
8 }
9}
متد intercept هر درخواست را به Observable تبدیل میکند که در ادامه قرار است با فراخوانی ()next.handle تعیین شود. بنابراین در این پیادهسازی روش کار کاملاً ساده است. درخواست را میگیرید، url آن را لاگ میکنید و متد ()next.handle را فرا میخوانید تا بدون ایجاد هیچ گونه تغییری آن را به سرور ارسال کنید.
شیء next نماینده رهگیر next در زنجیره رهگیرها است، چون میتوانید چندین رهگیر در اپلیکیشن خود داشته باشید. سپس next نهایی در این زنجیره عملاً «دستگیره بکاند» (HttpClient backend handler) محسوب میشود که در عمل درخواست را به سرور ارسال میکند.
ارائه Interceptor
از آنجا که Interceptor-ها وابستگیهای HttpClient محسوب میشوند باید آنها را در همان تزریق کننده (یا والد) که HttpClient را ارائه میکند، ارائه نمایید. برای نمونه فرض کنید HttpClientModule را در AppModule ایمپورت کردهاید و باید Interceptor-ها را به providers نیز اضافه کنید:
1...
2import { HTTP_INTERCEPTORS } from '@angular/common/http';
3import { RequestLogInterceptor } from '...';
4@NgModule({
5 ...
6 imports: [
7 HttpClientModule,
8 ...
9 ],
10 providers: [
11 {
12 provide: HTTP_INTERCEPTORS,
13 useClass: RequestLogInterceptor,
14 multi: true
15 },
16 ...
17 ],
18 ...
19})
20export class AppModule { }
گزینه multi: true ارائه شده به انگولار اعلام میکند که چندین رهگیر ارائه خواهید کرد و در این حالت استفاده از آن ضروری است. در سناریوی مثالی ما این گزینه ضرورتی ندارد، چون ما تنها یک رهگیر پیادهسازی کردهایم. بنابراین این بخش اطلاعات را صرفاً برای تذکر هایلایت کردیم. نکته مهم دیگر که باید به خاطر داشته باشید این است که انگولار رهگیرها را به همان ترتیبی که در providers ماژول ارائه میکنید اعمال میکند.
مدیریت احراز هویت
در این بخش به برسی یکی از کاربردهای رایج رهگیرها میپردازیم که مدیریت احراز هویت در یک اپلیکیشن است. این بار در عمل درخواست http را با یک interceptor تغییر میدهیم تا هدرهای احراز هویت را به آن اضافه کنیم.
با این فرض که اپلیکیشن اطلاعات کاربر وارد شده را در localstorage تعیین کرده است قصد داریم آن را خوانده و بررسی کنیم آیا کسی وارد شده است یا نه. در یک اپلیکیشن واقعی باید یک سرویس داشته باشید که همه گردش کار احراز هویت را مدیریت کند، اما با توجه به مقاصد آموزشی این مقاله کار را سادهتر کردهایم و اطلاعات را مستقیماً از localstorage میگیریم.
1@Injectable()
2export class AuthenticationInterceptor implements HttpInterceptor {
3 intercept(
4 request: HttpRequest<any>, next: HttpHandler
5 ) : Observable<HttpEvent<any>> {
6 const storageUser = localStorage.getItem('LoggedUser');
7 const loggedUser = jsonInfo ? JSON.parse(jsonInfo) : null;
8 if (loggedUser) {
9 request = request.clone({
10 headers: req.headers.set(
11 'Authorization',
12 loggedUser.authToken
13 )
14 });
15 }
16 return next.handle(request);
17 }
18}
از آنجا که باید درخواستها را در اپلیکیشن خود باز کنیم، (معنی آن این است که همه درخواستها نیازمند احراز هویت نیستند) قصد نداریم در صورتی که کاربر وارد حساب خود در اپلیکیشن نشده باشد، هیچ گونه خطای پیشفرضی صادر کنیم. با این حال در این وضعیت یک توکن احراز هویت به سرور ارسال میکنیم.
از سوی دیگر همچنان ممکن است یک خطای احراز هویت از سوی سرور دریافت کنیم که میتواند ناشی از ارسال توکن نادرست یا منقضی شده باشد. بنابراین این وضعیت را نیز باید AuthenticationInterceptor حل کنیم. کاری که میخواهیم بکنیم این است که در حالت ناموفق بودن درخواست، بررسی میکنیم آیا خطای احراز هویت (401 Unauthorized) وجود دارد یا نه. اگر چنین بود باید کاربر را از حساب خود در اپلیکیشن خارج و او را به صفحه ورود هدایت کنیم که اعلام میکند دسترسی وی لغو شده است. در ادامه قصد داریم این وضعیت را به AnthenticationInterceptor اضافه کنیم:
1@Injectable()
2export class AuthenticationInterceptor implements HttpInterceptor {
3 constructor(
4 private _router: Router
5 ) { }
6 intercept(
7 request: HttpRequest<any>, next: HttpHandler
8 ) : Observable<HttpEvent<any>> {
9 const storageUser = localStorage.getItem('LoggedUser');
10 const loggedUser = jsonInfo ? JSON.parse(jsonInfo) : null;
11 if (loggedUser) {
12 request = request.clone({
13 headers: req.headers.set(
14 'Authorization',
15 loggedUser.authToken
16 )
17 });
18 }
19 return next.handle(request).pipe(
20 catchError(error => {
21 // Checking if it is an Authentication Error (401)
22 if (error.status === 401) {
23 alert('Access Denied');
24 // <Log the user out of your application code>
25 this.router.navigate([ 'login-page-route' ]);
26 return throwError(error);
27 }
28 // If it is not an authentication error, just throw it
29 return throwError(error);
30 })
31 );
32 }
33}
با فراخوانی catchError از طریق یک pipe میتوانیم خطاهای پاسخ از سوی سرور را برای آن درخواست مدیریت کنیم از این رو قادر خواهیم بود حالت آن را بررسی کنیم و رفتار مناسبی که قبلاً اشاره شد را انجام دهیم.
توجه کنید که این رفتار با توجه به مقاصد آموزشی این راهنما ارائه شده است، اما در اپلیکیشنهای واقعی میتوان هر نوع رفتاری را تنظیم کرد. ممکن است بخواهید کاربر را به جایی هدایت نکنید و یا شاید بخواهید در صورت وقوع خطای 401 یک فراخوانی api به صورت refresh token اجرا کنید. در هر حال میتوانید هر کاری که برای اپلیکیشن شما ضروری است انجام دهید. در نهایت آن را در ماژول خود اضافه میکنیم.
1...
2import { HTTP_INTERCEPTORS } from '@angular/common/http';
3import { AuthenticationInterceptor } from '...';
4@NgModule({
5 ...
6 providers: [
7 {
8 provide: HTTP_INTERCEPTORS,
9 useClass: AuthenticationInterceptor
10 },
11 ...
12 ],
13 ...
14})
15export class AppModule { }
مدیریت جعل هویت
در این بخش نوع دیگری از رهگیری را مورد بررسی قرار میدهیم. فرض کنید میخواهیم نوعی جهل هویت در اپلیکیشن خود داشته باشیم. منظور ما از جعل هویت این است که میخواهید در جایی از اپلیکیشن به کاربری (مانند مدیر سیستم) اجازه بدهید که درخواستهای سرور را طوری ارسال کند که گویی کاربر دیگری است.
فرض کنید یک سرویس به نام ImpersonationService در اپلیکیشن خود دارید که همه منطق جعل هویت در آن اجرا میشود. قصد نداریم به جزییات این سرویس بپردازیم. از این سرویس اطلاعاتی را در مورد این که درخواست باید جعل شود یا نه دریافت میکنیم. در مواردی که لازم باشد جعل صورت گیرد، اطلاعات لازم برای سرور مانند شناسه کاربری که قرار است جعل شود از سوی سرویس در اختیار ما قرار میگیرد.
بدین ترتیب ایده رهگیر ما ساده است:
- بررسی میکنیم آیا درخواست باید جعل شود یا نه.
- اگر قرار بر جعل باشد، هدر مناسب را در درخواست ارسال میکنیم.
1@Injectable()
2export class ImpersonationInterceptor implements HttpInterceptor {
3 constructor(
4 private _impersonationService: ImpersonationService
5 ) { }
6 intercept(
7 request: HttpRequest<any>, next: HttpHandler
8 ) : Observable<HttpEvent<any>> {
9 const impersonatedUser: User | null =
10 this._impersonationService.getImpersonatedUser();
11 if (impersonatedUser && impersonatedUser.id) {
12 request = request.clone({
13 setHeaders: {
14 'ImpersonatedUserId': impersonatedUser.id
15 }
16 });
17 }
18}
در این مورد نیز باید رهگیر جدید را به ماژول خود اضافه کنیم و به این منظور آن را به AuthenticationInterceptor که قبلاً ایجاد کردهایم میافزاییم.
1...
2import { HTTP_INTERCEPTORS } from '@angular/common/http';
3import { AuthenticationInterceptor } from '...';
4import { ImpersonationInterceptor } from '...';
5@NgModule({
6 ...
7 providers: [
8 {
9 provide: HTTP_INTERCEPTORS,
10 useClass: AuthenticationInterceptor,
11 multi: true
12 },
13 {
14 provide: HTTP_INTERCEPTORS,
15 useClass: ImpersonationInterceptor,
16 multi: true
17 },
18 ...
19 ],
20 ...
21})
22export class AppModule { }
سخن پایانی
استفاده از رهگیر در موارد مختلف و در انواع متفاوتی از اپلیکیشنها بسیار مفید است. از این رو آشنایی با طرز کار آن و کارهایی که با بهرهگیری از آن میتوان انجام داد حائز اهمیت تلقی میشود.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای JavaScript (جاوا اسکریپت)
- مجموعه آموزشهای برنامهنویسی
- آموزش انگولار (Angular): ساخت یک اپلیکیشن در ۲۰ دقیقه – به زبان ساده
- اسکرول بینهایت در اپلیکیشنهای انگولار — از صفر تا صد
- ۱۰ قابلیت مفید انگولار که احتمالاً از وجودشان اطلاع ندارید — راهنمای کاربردی
==
باسلام، انواع interceptor که پیاده سازی میشن چه مواردی هستن؟