能否像HttpInterceptor一样拦截Angular中的Apollo/GraphQL请求?
当然可以!其实Apollo Client本身就提供了一套和Angular HttpInterceptor异曲同工的拦截机制——核心就是Apollo Link,这是Apollo生态专门为GraphQL请求设计的链路处理方案,比通用的HttpInterceptor更贴合GraphQL的场景。
首先得明确:Apollo Client并没有使用Angular的HttpClient来发送请求,它是自己封装了HTTP(或WebSocket)请求逻辑,所以HttpInterceptor根本抓不到这些请求,这就是为什么之前的拦截逻辑对Apollo无效。
Apollo Link的本质是一个请求处理链,每个Link可以负责请求的一个环节(比如添加头、处理错误、修改操作等),和HttpInterceptor的拦截流程非常相似,甚至更灵活。
1. 自定义请求拦截(对应HttpInterceptor的请求处理)
你可以创建一个自定义Link,在请求发送前修改操作、添加头信息,或者在响应返回后处理数据:
import { ApolloLink } from 'apollo-link'; import { HttpLink } from 'apollo-angular-link-http'; import { InMemoryCache } from 'apollo-cache-inmemory'; import { APOLLO_OPTIONS } from 'apollo-angular'; // 自定义请求拦截Link const customRequestLink = new ApolloLink((operation, forward) => { // 1. 请求发送前的处理:比如添加自定义Headers const currentHeaders = operation.getContext().headers || {}; operation.setContext({ headers: { ...currentHeaders, 'X-App-Version': '1.0.0', // 自定义业务头 Authorization: `Bearer ${localStorage.getItem('authToken')}` // 添加认证Token } }); // 2. 响应返回后的处理:修改返回数据或做后续操作 return forward(operation).map(response => { // 示例:给所有响应添加一个自定义标识 response.data.__processed = true; return response; }); }); // 组合链路:把自定义Link和HTTP Link串起来 const httpLink = new HttpLink({ uri: '/graphql' }); const apolloLink = ApolloLink.from([ customRequestLink, httpLink ]); // 在Apollo Client配置中使用这个链路 @NgModule({ providers: [ { provide: APOLLO_OPTIONS, useFactory: () => ({ link: apolloLink, cache: new InMemoryCache() }) } ] }) export class GraphQLModule {}
2. 错误拦截(结合你正在用的apollo-link-error)
你已经在使用apollo-link-error,其实它本身就是一个专门处理错误的Link,你可以把它整合到链路中,和请求拦截Link配合使用,实现类似HttpInterceptor的错误处理逻辑:
import { onError } from 'apollo-link-error'; import { Router } from '@angular/router'; import { NotificationService } from './your-notification-service'; // 注入依赖(注意:如果在模块工厂中使用,需要调整注入方式) const router = inject(Router); const notificationService = inject(NotificationService); // 错误处理Link const errorLink = onError(({ graphQLErrors, networkError, operation }) => { // 处理GraphQL层面的错误(比如查询语法错误、权限不足等) if (graphQLErrors) { graphQLErrors.forEach(err => { console.error(`GraphQL错误[${err.path}]: ${err.message}`); // 示例:如果是未授权错误,跳转到登录页 if (err.extensions?.code === 'UNAUTHENTICATED') { router.navigate(['/login']); } }); } // 处理网络层面的错误(比如请求超时、服务器宕机) if (networkError) { console.error(`网络错误: ${networkError.message}`); // 示例:显示全局网络错误提示 notificationService.showError('网络连接失败,请稍后重试'); } }); // 更新链路:把错误Link放在最前面(确保错误能被最先捕获) const apolloLink = ApolloLink.from([ errorLink, customRequestLink, httpLink ]);
进阶:通过Context传递自定义数据
和HttpInterceptor里的HttpRequest可以携带自定义数据类似,Apollo的operation也可以通过context传递信息,让你的拦截逻辑更灵活。比如在组件中发起请求时设置自定义标识:
// 组件中发起查询 this.apollo.query({ query: USER_PROFILE_QUERY, context: { skipErrorHandling: true // 自定义字段:跳过全局错误处理 } }).subscribe(...);
然后在错误Link中读取这个标识:
const errorLink = onError(({ graphQLErrors, networkError, operation }) => { const skipErrorHandling = operation.getContext().skipErrorHandling; if (skipErrorHandling) return; // 如果设置了跳过,就不处理错误 // 正常的错误处理逻辑... });
完全可以用Apollo Link实现和HttpInterceptor几乎一样的拦截效果,而且它更适配GraphQL的请求场景——能直接访问GraphQL的操作细节(比如查询名、变量、上下文)。建议直接用这种原生方案,不要尝试让Apollo请求走HttpClient(那样会丢失Apollo的缓存、批量请求等核心特性)。
内容的提问来源于stack exchange,提问作者ezay

