面试题答案
一键面试减少重复请求
- 使用RxJS的shareReplay操作符:
- 在Angular服务中,当发起HTTP请求时,可使用
shareReplay
操作符。例如:
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { shareReplay } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class DataService { private data$: Observable<any>; constructor(private http: HttpClient) {} getData(): Observable<any> { if (!this.data$) { this.data$ = this.http.get('/api/data').pipe( shareReplay(1) ); } return this.data$; } }
shareReplay(1)
会缓存最新的一次值,并将其重播给新的订阅者,这样后续相同的请求就无需再次向服务器发送。
- 在Angular服务中,当发起HTTP请求时,可使用
- 自定义缓存机制:
- 创建一个简单的内存缓存对象,在服务中管理请求和响应。
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class CustomCacheService { private cache = {}; constructor(private http: HttpClient) {} get<T>(url: string): Observable<T> { if (this.cache[url]) { return new Observable(observer => { observer.next(this.cache[url]); observer.complete(); }); } return this.http.get<T>(url).pipe( tap(data => { this.cache[url] = data; }) ); } }
合理设置缓存策略
- HTTP缓存头:
- 在后端设置合适的HTTP缓存头,如
Cache - Control
和ETag
。例如,后端可设置Cache - Control: max - age = 3600
,表示响应可被缓存1小时。 - 在Angular中,可通过
HttpHeaders
来传递缓存相关信息。例如:
import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class CacheHeaderService { constructor(private http: HttpClient) {} getData(): Observable<any> { const headers = new HttpHeaders({ 'Cache - Control':'max - age = 0' }); return this.http.get('/api/data', { headers }); } }
- 在后端设置合适的HTTP缓存头,如
- 拦截器实现缓存:
- 创建一个HTTP拦截器,在拦截器中处理缓存逻辑。例如:
import { Injectable } from '@angular/core'; import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; @Injectable() export class CacheInterceptor implements HttpInterceptor { private cache = {}; intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { if (request.method === 'GET' && this.cache[request.url]) { return new Observable(observer => { observer.next(this.cache[request.url]); observer.complete(); }); } return next.handle(request).pipe( tap((event: HttpEvent<any>) => { if (event instanceof HttpResponse && request.method === 'GET') { this.cache[request.url] = event; } }) ); } }
- 然后在
app.module.ts
中注册该拦截器:
providers: [ { provide: HTTP_INTERCEPTORS, useClass: CacheInterceptor, multi: true } ]
确保数据传输的安全性
- 防止CSRF攻击:
- 后端生成和验证Token:后端为每个用户会话生成一个唯一的CSRF Token,并在响应中返回给前端,例如通过设置
X - CSRF - Token
响应头。 - 前端传递Token:在Angular中,使用HTTP拦截器在每个非GET请求中添加CSRF Token到请求头中。
import { Injectable } from '@angular/core'; import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable() export class CsrfInterceptor implements HttpInterceptor { constructor() {} intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { const csrfToken = localStorage.getItem('csrf - token'); if (csrfToken && (request.method === 'POST' || request.method === 'PUT' || request.method === 'DELETE')) { request = request.clone({ setHeaders: { 'X - CSRF - Token': csrfToken } }); } return next.handle(request); } }
- 在
app.module.ts
中注册该拦截器:
providers: [ { provide: HTTP_INTERCEPTORS, useClass: CsrfInterceptor, multi: true } ]
- 后端生成和验证Token:后端为每个用户会话生成一个唯一的CSRF Token,并在响应中返回给前端,例如通过设置
- 防止XSS攻击:
- 使用DomSanitizer:在显示从后端获取的数据时,使用
DomSanitizer
对数据进行安全处理。例如:
import { Component, OnInit } from '@angular/core'; import { DomSanitizer, SafeHtml } from '@angular/platform - browser'; import { HttpClient } from '@angular/common/http'; @Component({ selector: 'app - xss - safe', templateUrl: './xss - safe.component.html' }) export class XssSafeComponent implements OnInit { data: SafeHtml; constructor(private http: HttpClient, private sanitizer: DomSanitizer) {} ngOnInit() { this.http.get('/api/xss - data', { responseType: 'text' }).subscribe(data => { this.data = this.sanitizer.bypassSecurityTrustHtml(data); }); } }
- 在模板中直接使用
[innerHTML]="data"
来显示安全的HTML内容。同时,避免使用eval()
或动态创建并执行JavaScript代码,以防止恶意脚本注入。
- 使用DomSanitizer:在显示从后端获取的数据时,使用