Angular中RxJS实现HTTP返回204时重复含新ID请求流程的问题
RxJS实现基于响应状态码的条件重试(带ID更新)
问题描述
需求
- 使用已保存的ID发送停止请求;
- 生成新ID并发送启动请求,同时保存该新ID;
- 若响应状态码为200则正常结束,若为204则从第一步开始重试(重试时必须用新ID)。
实现约束
尽量避免多层subscribe,仅在末尾使用一次subscribe,其余逻辑通过RxJS的pipe操作完成。
当前代码
服务端方法:
public doStuff(): Observable<string> { if (this.id) { return this.httpClient.get(`${STOP_URL}/${this.id}`).pipe( tap(() => { this.id = Date.now(); }), mergeMap(() => this.httpClient.get(`${START_URL}/${this.id}`)) ); } else {/* 基本逻辑相同,只是无需发送停止请求 */} }
组件调用代码:
this.service .doStuff() .pipe( // TODO:当响应为204时,使用新ID重复流程 mergeMap(result => this.httpClient.get(/* 使用响应部分内容的请求 */)) ).subscribe(foo => bar())
遇到的问题
无法在TODO位置实现以下逻辑:
(a) 识别响应状态码;
(b) 重试时生成新ID(至少实现(a))。
现有障碍:retryWhen仅对非2XX错误生效,repeat无法添加条件判断,repeatWhen无法获取响应内容。
解决方案
核心思路是用expand操作符——它能基于当前流的输出决定是否继续发射新的Observable,刚好适配这种需要根据响应内容判断重试的场景,同时把ID更新和状态码判断逻辑封装到流程内部。
步骤1:重构doStuff方法,返回包含状态码的完整响应
首先修改HTTP请求配置,让它返回完整的响应对象(包含状态码),而不是仅返回响应体:
import { of, Observable } from 'rxjs'; import { tap, mergeMap, map } from 'rxjs/operators'; public doStuff(): Observable<{ data: string; status: number }> { // 无ID时跳过停止请求,模拟一个成功响应 const stopRequest$ = this.id ? this.httpClient.get(`${STOP_URL}/${this.id}`, { observe: 'response' }) : of({ status: 200 }); return stopRequest$.pipe( tap(() => { this.id = Date.now(); // 生成新ID并保存 }), // 发送启动请求,同样获取完整响应 mergeMap(() => this.httpClient.get(`${START_URL}/${this.id}`, { observe: 'response' })), // 整理成包含数据和状态码的对象 map(response => ({ data: response.body as string, status: response.status })) ); }
步骤2:在组件中用expand实现条件重试
利用expand判断响应状态码,决定是否重试;同时用filter只保留最终成功的响应:
import { EMPTY } from 'rxjs'; import { expand, filter, mergeMap } from 'rxjs/operators'; this.service .doStuff() .pipe( // 条件重试:状态码为204时重新执行doStuff,否则终止流 expand(({ status }) => status === 204 ? this.service.doStuff() : EMPTY), // 只保留状态码200的成功响应 filter(({ status }) => status === 200), // 用成功响应的数据发起后续请求 mergeMap(({ data }) => this.httpClient.get(/* 使用data的请求路径 */)) ) .subscribe(foo => bar());
方案优势
- 完全符合“仅末尾一次subscribe”的要求,所有逻辑通过
pipe串联; expand可以直接访问响应状态码,解决了repeatWhen无法获取响应信息的问题;- 每次重试时
doStuff内部都会自动生成新ID,满足重试用新ID的需求; - 逻辑清晰,没有多层嵌套,易于维护。
内容的提问来源于stack exchange,提问作者Panossa
相关产品推荐
相关产品推荐

