MST

星途 面试题库

面试题:Angular HttpClient模块请求头设置在复杂场景下的优化与安全考量

在一个大型企业级Angular项目中,前端与多个后端微服务交互,存在跨域、请求频率限制、安全认证等复杂场景。请阐述如何基于HttpClient模块对请求头进行设置与管理,以实现性能优化、安全加固以及高效的维护,并举例说明可能遇到的问题及解决方案。
49.3万 热度难度
前端开发Angular

知识考点

AI 面试

面试题答案

一键面试

设置与管理请求头以优化性能、加固安全和高效维护

  1. 性能优化
    • 缓存控制:通过设置Cache - Control请求头来控制缓存策略。例如,对于不经常变化的数据请求,可以设置Cache - Control: public, max - age = 3600,表示允许公共缓存,缓存有效期为1小时。在Angular中使用HttpClient时:
    import { HttpClient, HttpHeaders } from '@angular/common/http';
    const headers = new HttpHeaders().set('Cache - Control', 'public, max - age = 3600');
    this.http.get('/api/data', { headers }).subscribe(response => {
        // 处理响应
    });
    
    • 压缩:启用压缩以减少数据传输量。设置Accept - Encoding头为gzip, deflate,让服务器知道客户端支持的压缩方式。
    const headers = new HttpHeaders().set('Accept - Encoding', 'gzip, deflate');
    this.http.get('/api/data', { headers }).subscribe(response => {
        // 处理响应
    });
    
  2. 安全加固
    • 认证:使用Authorization头进行身份验证。例如,对于Bearer令牌认证,设置Authorization: Bearer <token>。假设从本地存储获取令牌:
    const token = localStorage.getItem('token');
    const headers = new HttpHeaders().set('Authorization', `Bearer ${token}`);
    this.http.get('/api/protected - data', { headers }).subscribe(response => {
        // 处理响应
    });
    
    • 防止跨站请求伪造(CSRF):如果后端使用CSRF保护,前端需要在请求头中包含CSRF令牌。假设从cookie中获取CSRF令牌:
    import { HttpHeaders } from '@angular/common/http';
    const csrfToken = getCsrfTokenFromCookie(); // 自定义函数获取CSRF令牌
    const headers = new HttpHeaders().set('X - CSRF - Token', csrfToken);
    this.http.post('/api/some - action', { data }, { headers }).subscribe(response => {
        // 处理响应
    });
    
  3. 高效维护
    • 使用拦截器:创建一个HttpInterceptor来统一处理请求头设置。例如,创建一个AuthInterceptor来处理认证相关的请求头:
    import { Injectable } from '@angular/core';
    import {
        HttpEvent,
        HttpHandler,
        HttpInterceptor,
        HttpRequest
    } from '@angular/common/http';
    import { Observable } from 'rxjs';
    
    @Injectable()
    export class AuthInterceptor implements HttpInterceptor {
        intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
            const token = localStorage.getItem('token');
            if (token) {
                request = request.clone({
                    setHeaders: {
                        Authorization: `Bearer ${token}`
                    }
                });
            }
            return next.handle(request);
        }
    }
    
    然后在app.module.ts中注册拦截器:
    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform - browser';
    import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
    import { AuthInterceptor } from './auth - interceptor';
    
    @NgModule({
        imports: [BrowserModule, HttpClientModule],
        providers: [
            {
                provide: HTTP_INTERCEPTORS,
                useClass: AuthInterceptor,
                multi: true
            }
        ],
        bootstrap: []
    })
    export class AppModule {}
    

可能遇到的问题及解决方案

  1. 跨域问题
    • 问题:由于前端与多个后端微服务交互,可能会遇到跨域问题,浏览器会阻止请求。
    • 解决方案
      • 后端配置:在后端微服务中配置CORS(跨域资源共享)。例如,在Node.js中使用cors库:
        const express = require('express');
        const cors = require('cors');
        const app = express();
        app.use(cors());
        
      • 代理服务器:在前端开发环境中,可以使用代理服务器。在Angular项目中,可以通过在angular.json中配置代理:
        {
            "architect": {
                "serve": {
                    "proxyConfig": "./proxy.conf.json"
                }
            }
        }
        
        proxy.conf.json中配置:
        {
            "/api": {
                "target": "http://backend - microservice - url",
                "secure": false,
                "changeOrigin": true
            }
        }
        
  2. 请求频率限制问题
    • 问题:后端可能对请求频率进行限制,频繁请求可能导致请求被拒绝。
    • 解决方案
      • 客户端缓存:如上述性能优化中的缓存控制,减少不必要的请求。
      • 节流与防抖:使用RxJS的throttleTimedebounceTime操作符。例如,对于搜索框的自动完成功能,防止用户快速输入导致过多请求:
        import { Component } from '@angular/core';
        import { fromEvent, Observable } from 'rxjs';
        import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators';
        
        @Component({
            selector: 'app - search',
            templateUrl: './search.component.html'
        })
        export class SearchComponent {
            constructor(private http: HttpClient) {
                const searchInput = document.getElementById('search - input') as HTMLInputElement;
                const search$: Observable<string> = fromEvent(searchInput, 'input').pipe(
                    map((event: any) => event.target.value),
                    debounceTime(300),
                    distinctUntilChanged()
                );
                search$.subscribe(query => {
                    // 发送请求
                    const headers = new HttpHeaders();
                    this.http.get(`/api/search?q = ${query}`, { headers }).subscribe(response => {
                        // 处理响应
                    });
                });
            }
        }
        
  3. 认证相关问题
    • 问题:令牌过期或认证失败导致请求被拒绝。
    • 解决方案
      • 令牌刷新:当收到认证失败(如401状态码)的响应时,自动刷新令牌并重试请求。可以在拦截器中处理:
        import { Injectable } from '@angular/core';
        import {
            HttpEvent,
            HttpHandler,
            HttpInterceptor,
            HttpRequest
        } from '@angular/common/http';
        import { Observable, catchError, switchMap } from 'rxjs';
        
        @Injectable()
        export class AuthInterceptor implements HttpInterceptor {
            constructor(private authService: AuthService) {}
        
            intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
                return next.handle(request).pipe(
                    catchError(error => {
                        if (error.status === 401) {
                            return this.authService.refreshToken().pipe(
                                switchMap(() => {
                                    const newToken = this.authService.getToken();
                                    request = request.clone({
                                        setHeaders: {
                                            Authorization: `Bearer ${newToken}`
                                        }
                                    });
                                    return next.handle(request);
                                })
                            );
                        }
                        throw error;
                    })
                );
            }
        }
        
      • 重新登录:如果令牌刷新失败,引导用户重新登录。可以在上述拦截器的catchError中添加逻辑,如导航到登录页面:
        import { Injectable } from '@angular/core';
        import {
            HttpEvent,
            HttpHandler,
            HttpInterceptor,
            HttpRequest
        } from '@angular/common/http';
        import { Observable, catchError, switchMap } from 'rxjs';
        import { Router } from '@angular/router';
        
        @Injectable()
        export class AuthInterceptor implements HttpInterceptor {
            constructor(private authService: AuthService, private router: Router) {}
        
            intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
                return next.handle(request).pipe(
                    catchError(error => {
                        if (error.status === 401) {
                            return this.authService.refreshToken().pipe(
                                switchMap(() => {
                                    const newToken = this.authService.getToken();
                                    request = request.clone({
                                        setHeaders: {
                                            Authorization: `Bearer ${newToken}`
                                        }
                                    });
                                    return next.handle(request);
                                }),
                                catchError(() => {
                                    this.router.navigate(['/login']);
                                    throw error;
                                })
                            );
                        }
                        throw error;
                    })
                );
            }
        }