Angular 8中Interceptor使用及HttpClient替代API调用报错解决
解决Angular 8 API迁移错误与Interceptor使用指南
咱们先来看你遇到的SyntaxError: Unexpected end of input问题,再一步步讲如何用Interceptor简化API调用逻辑。
一、修复当前API调用的语法错误
这个错误本质是前端尝试解析无效的JSON响应,根源是你新写的HttpClient调用没有复现原来自定义invoke方法里的核心逻辑:
1. 原来的invoke做了哪些你没做的事?
- 自动替换路由里的占位符(比如
{patientUUID}会替换成用户的profile.participant值) - 拼接环境变量里的基础URL(
environment.ApiOrigin) - 添加
Authorization: Bearer {token}请求头 - 统一处理错误和响应类型
你的新代码直接调用HttpClient.get,导致请求URL还是带着{patientUUID}占位符,后端返回404或非JSON响应,前端解析时就会报错。
2. 修正getDossierEntry方法
先在DocumentCorrespondenceService里补上这些缺失的逻辑,同时保留类型安全:
import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { DossierEntry } from '../../interfaces/dossier/dossier-entry.interface'; import { AuthService } from '../auth/auth.service'; // 替换成你的Auth服务路径 import { environment } from '../../../environments/environment'; import { map, switchMap, take, catchError } from 'rxjs/operators'; // 先定义API响应的类型(如果原API返回带values的对象) interface DossierEntryResponse { values: DossierEntry[]; } @Injectable({ providedIn: 'root' }) export class DocumentCorrespondenceService { constructor( private http: HttpClient, private authService: AuthService ) {} getDossierEntry(type: string = ''): Observable<DossierEntryResponse> { const entryType = type === '' ? 'all' : `type/${type}`; const rawRoute = `/api/patient/{patientUUID}/DossierEntry/${entryType}`; return this.authService.loginStatus().pipe( take(1), // 只取一次用户登录状态 map(user => { if (!user) { throw Error('需要登录才能访问该API'); } // 替换路由占位符 const parsedRoute = rawRoute.replace('{patientUUID}', user.profile.participant); // 拼接基础URL return `${environment.ApiOrigin}${parsedRoute}`; }), switchMap(url => { // 构建请求头 const headers = new HttpHeaders({ 'Content-Type': 'application/json', 'Accept': 'application/json' }); // 添加Token头 const accessToken = this.authService.getAccessToken(); const authHeaders = accessToken ? headers.set('Authorization', `Bearer ${accessToken}`) : headers; // 发起类型安全的请求 return this.http.get<DossierEntryResponse>(url, { headers: authHeaders }); }), // 复用原有的错误处理逻辑 catchError(err => this.handleError(err)) ); } // 复制原HealthAPIService中的handleError方法到这里 private handleError(error: any): Observable<never> { console.error('API请求失败:', error); // 根据原逻辑调整,比如弹出提示、返回空数据等 return throwError(() => new Error(error.message || '服务器错误')); } }
3. 组件中的响应处理修正
你的组件代码里尝试访问result.values,但原自定义方法返回的是包含values的对象,所以要修改组件的赋值逻辑:
ngOnInit() { this.documentCorrespondencService.getDossierEntry('physical').subscribe(result => { console.log(result.values); this.entries = result.values; // 这里要赋值values数组,不是整个result对象 this.dossiersLoaded = true; }, err => { console.error('加载病历失败:', err); this.dossiersLoaded = true; // 加载失败也要标记状态,避免页面一直加载 }); }
二、Angular 8中使用Interceptor的完整步骤
Interceptor可以帮你把所有API的通用逻辑(加Token、替换占位符、错误处理)统一起来,不用每个方法都重复写代码,完美替代你原来的invoke方法。
1. 创建Auth拦截器(处理Token和基础URL)
用Angular CLI生成拦截器:ng generate interceptor auth,然后修改代码:
import { Injectable } from '@angular/core'; import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { AuthService } from './auth.service'; // 替换成你的Auth服务路径 import { environment } from '../environments/environment'; @Injectable() export class AuthInterceptor implements HttpInterceptor { constructor(private authService: AuthService) {} intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> { // 1. 拼接基础URL(仅针对相对路径的请求) let modifiedUrl = request.url; if (!modifiedUrl.startsWith('http')) { modifiedUrl = `${environment.ApiOrigin}${modifiedUrl}`; } // 2. 添加Authorization Token头 const accessToken = this.authService.getAccessToken(); let modifiedRequest = request.clone({ url: modifiedUrl }); if (accessToken) { modifiedRequest = modifiedRequest.clone({ setHeaders: { Authorization: `Bearer ${accessToken}` } }); } // 3. 统一处理错误 return next.handle(modifiedRequest).pipe( catchError((error: HttpErrorResponse) => { // 这里可以统一处理401跳登录、403无权限等场景 console.error('HTTP请求错误:', error); return throwError(() => new Error(error.message || '服务器请求失败')); }) ); } }
2. 创建路由占位符拦截器(替换{patientUUID}等)
再生成一个拦截器处理路由替换:ng generate interceptor route-placeholder
import { Injectable } from '@angular/core'; import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http'; import { Observable } from 'rxjs'; import { AuthService } from './auth.service'; import { switchMap, take } from 'rxjs/operators'; @Injectable() export class RoutePlaceholderInterceptor implements HttpInterceptor { constructor(private authService: AuthService) {} intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> { // 检查URL是否包含需要替换的占位符 if (request.url.includes('{patientUUID}') || request.url.includes('{userId}')) { return this.authService.loginStatus().pipe( take(1), switchMap(user => { if (!user) { throw Error('未登录用户无法访问该API'); } let parsedUrl = request.url; parsedUrl = parsedUrl.replace('{userId}', user.profile.sub); parsedUrl = parsedUrl.replace('{patientUUID}', user.profile.participant); const modifiedRequest = request.clone({ url: parsedUrl }); return next.handle(modifiedRequest); }) ); } return next.handle(request); } }
3. 在AppModule中注册拦截器
打开app.module.ts,把两个拦截器添加到providers数组:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { AuthInterceptor } from './auth.interceptor'; import { RoutePlaceholderInterceptor } from './route-placeholder.interceptor'; import { AppComponent } from './app.component'; @NgModule({ declarations: [AppComponent], imports: [BrowserModule, HttpClientModule], providers: [ { provide: HTTP_INTERCEPTORS, useClass: RoutePlaceholderInterceptor, multi: true // 允许多个拦截器生效 }, { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } ], bootstrap: [AppComponent] }) export class AppModule {}
4. 简化API方法
有了拦截器之后,getDossierEntry可以简化到极致:
getDossierEntry(type: string = ''): Observable<DossierEntryResponse> { const entryType = type === '' ? 'all' : `type/${type}`; const url = `/api/patient/{patientUUID}/DossierEntry/${entryType}`; return this.http.get<DossierEntryResponse>(url); }
所有通用逻辑都由拦截器自动处理,代码更简洁,也更符合Angular的最佳实践。
内容的提问来源于stack exchange,提问作者mightycode Newton
相关产品推荐
相关产品推荐

