You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular HttpClient请求未执行问题排查求助

Angular HttpClient请求无法触发排查

问题描述

无法触发HttpClient请求,推测是导入或配置问题,但找不到原因。API本身可用,但从未被调用。

环境版本

  • Angular: 15.1.2
  • CLI: 15.1.3
  • Node: 16.14.2
  • Package Manager: npm 8.5.0
  • OS: win32 x64

项目结构

主着陆页包含多个widgets(快速查看内容的组件),每个widget对应独立组件。为widget提供数据的Service使用providedIn: 'root',并暴露Observable供组件订阅。

相关代码

app.module.ts

const pipes = [
  SignUpFormTextPipe,
  StageFormatTextPipe,
  StageTypeTextPipe,
  TeamSizeTextPipe,
  TournamentStatusTextPipe,
];

const providers = [HttpClientModule];

@NgModule({
  declarations: [...components, ...pipes],
  imports: [
    BrowserModule,
    FormsModule,
    ReactiveFormsModule,
    AppRoutingModule,
    HttpClientModule,
  ],
  providers: [...pipes, ...providers],
  bootstrap: [AppComponent],
})

TournamentService

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Tournament } from '../models/tournament/tournament';
import { BehaviorSubject, Observable, take, tap } from 'rxjs';
import { TournamentsResponse } from '../models/api-responses/tournament/tournaments-response';

@Injectable({
  providedIn: 'root',
})
export class TournamentService {
  private baseUrl: 'http://localhost:5077/api/tournamens';

  initialTournaments: Tournament[] = [];
  private tournaments = new BehaviorSubject<Tournament[]>(
    this.initialTournaments
  );

  public tournaments$ = this.tournaments.asObservable();

  constructor(private httpClient: HttpClient) {}

  public getTournaments(): Observable<TournamentsResponse> {
    console.log('test 123'); // 此日志正常输出

    return this.httpClient.get<TournamentsResponse>(this.baseUrl).pipe(
      tap((resp) => {
        console.log(resp); // 此日志未输出

        this.tournaments.next(resp.tournaments);

        return resp;
      }),
      take(1)
    );
  }
}

订阅组件UpcomingTourneysComponent

import { Component, OnInit } from '@angular/core';
import { Observable, Subject, takeUntil } from 'rxjs';
import { Tournament } from 'src/app/models/tournament/tournament';
import { NotificationService } from 'src/app/services/common/notification.service';
import { TournamentService } from 'src/app/services/tournament.service';

@Component({
  selector: 'app-upcoming-tourneys',
  templateUrl: './upcoming-tourneys.component.html',
  styleUrls: ['./upcoming-tourneys.component.css'],
})
export class UpcomingTourneysComponent implements OnInit {
  private destroy$: Subject<void> = new Subject<void>();

  upcomingTournaments$: Observable<Tournament[]> =
    this.tournamentService.tournaments$;

  constructor(
    private tournamentService: TournamentService,
    private notificationService: NotificationService
  ) {}

  ngOnInit() {
    console.log(this.tournamentService); // 此日志正常输出

    this.tournamentService
      .getTournaments()
      .pipe(takeUntil(this.destroy$))
      .subscribe({
        next: (resp) => {
          console.log(resp); // 此日志未输出
        },
        error: (resp) => {
          this.notificationService.showError(resp);
        },
      });
  }
}

组件HTML

test if component is rendered
<div class="container-fluid" *ngIf="upcomingTournaments$ | async as upcomingTournaments">
  <div class="card">
    <div class="card-header" routerLink="/tournaments">
      Upcoming tournaments
    </div>
    <div class="card-body">
       <div *ngFor="let tourney of upcomingTournaments">
         ... content...
       </div>
    </div>
  </div>
</div>

我在另一个项目中使用相同模式可以正常工作,初步怀疑是导入错误,但IDE智能提示和代码检查工具未给出任何提示。


更新1:改用lastValueFrom仍无效

按照建议改用lastValueFrom(...),但HTTP请求仍未触发:

修改后的Service

@Injectable({ providedIn: 'root' })
export class TournamentService {
  private baseUrl: 'http://localhost:5077/api/tournamens';

  initialTournaments: Tournament[] = [];
  private tournamentsSubject = new BehaviorSubject<Tournament[]>(
    this.initialTournaments
  );

  public tournaments$ = this.tournamentsSubject.asObservable();

  constructor(private httpClient: HttpClient) {}

  public getTournaments() {
    const result = lastValueFrom(
      this.httpClient.get<TournamentsResponse>(this.baseUrl)
    ).then((resp) => {
      this.tournamentsSubject.next(resp.tournaments);
    });
  }
}

修改后的组件TS

export class UpcomingTourneysComponent implements OnInit {
  upcomingTournaments$: Observable<Tournament[]> =
    this.tournamentService.tournaments$;

  constructor(private tournamentService: TournamentService) {}

  ngOnInit() {
    this.tournamentService.getTournaments();
  }
}

修改后的组件HTML

test if component is rendered
{{ upcomingTournaments$ | async }}
<div
  class="container-fluid"
  *ngIf="upcomingTournaments$ | async as upcomingTournaments"
>
....
</div>

更新2:简化调用仍报错

尝试移除所有Observable相关逻辑,简化调用,但仍未触发GET请求,且出现错误:

修改后的Service

public getTournaments(): Observable<TournamentsResponse> {
    return this.httpClient.get<TournamentsResponse>(this.baseUrl);
  }

修改后的组件

ngOnInit() {
    const response = this.tournamentService.getTournaments().subscribe({
      next: (resp) => {
        console.log({ what: 'next call within service subscribe', resp }); // 输出错误:TypeError: Cannot read properties of undefined (reading 'toLowerCase')
      },
    });
    console.log({ what: 'upcomingTourneysInit', response }); // 此处Response为SafeSubscriber
  }

解决方案

问题根源

  1. baseUrl声明错误:代码中private baseUrl: 'http://localhost:5077/api/tournamens';使用了类型注解(冒号:)而非变量赋值,导致baseUrl实际为undefined。HttpClient处理undefined的URL时,内部会调用toLowerCase()方法,从而抛出TypeError,但RxJS错误未被直观捕获,导致看起来请求未触发。
  2. URL拼写错误:tournamens应为tournaments(少了一个t),即使修复赋值问题,这个错误也会导致API请求404。

修复步骤

  1. 修正baseUrl的赋值方式,同时修正拼写:
// 原错误代码
private baseUrl: 'http://localhost:5077/api/tournamens';

// 修正后
private baseUrl = 'http://localhost:5077/api/tournaments';
  1. 原有Observable订阅逻辑无需修改,保持即可。
  2. 验证API地址可访问,确认端口和路径正确。

验证修复

修正后,HttpClient会拿到正确的URL,正常触发请求,tap操作符中的日志会正常输出,组件订阅也能收到返回数据。


内容的提问来源于stack exchange,提问作者Neophyte

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 10:08:12