【问题标题】:Angular Universal has CORS issue on API req when routing to page, but not when I refresh the pageAngular Universal 在路由到页面时在 API req 上有 CORS 问题,但在我刷新页面时没有
【发布时间】:2021-10-03 13:58:50
【问题描述】:

在使用 AngularJS 一段时间后,我开始学习 Angular 2+,并且一直在使用 Angular Universal 进行 SSR 尝试并一路学习。

但是,我在 ngOnInit 中的 API 请求中遇到了这个 CORS 问题,其中一个视图加载了有关所选项目的其他详细信息。

只有当我从列表页面(通过路由器链接选择硬币的静态列表)导航到特定硬币的详细信息页面时,才会出现 CORS 问题。

在完成这些工作时,我将这两个教程用作指导帖子。

Angular SSR With HttpClient rehydration

Angular SSR

任何帮助都将不胜感激,我的前端技能不是很强,而且我对 CORS 也不是很熟悉。

路由器代码片段:

const routes: Routes = [
  { path: '', component: CoinListComponent },
  { path: 'coin/:symbol', component: CoinSnapshotComponent },
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, {
      initialNavigation: 'enabled',
    }),
  ],
  exports: [RouterModule],
})
export class AppRoutingModule {}

列表视图:

<h2>Current Coins</h2>
<ul *ngFor="let coin of coins">
  <li>
    <div>
      <a routerLink="/coin/{{coin.symbol}}"><span>{{coin.name}} - {{coin.symbol}}</span></a>
    </div>
  </li>
</ul>

硬币细节组件:

interface CoinData {
  [key: string]: number;
}
interface CoinDataResponse {
  data: {
    currency: string;
    rates: CoinData;
  };
}

@Component({
  selector: 'app-coin-snapshot',
  templateUrl: './coin-snapshot.component.html',
  styleUrls: ['./coin-snapshot.component.scss'],
})
export class CoinSnapshotComponent implements OnInit {
  coins = coins;
  coin: Coin;
  defaultCoin: Coin;
  coinData: CoinData | undefined;
  constructor(
    private route: ActivatedRoute,
    private http: CustomHttpClientService
  ) {
    this.defaultCoin = {
      symbol: 'DEFAULT',
      name: 'DEFAULT',
      link: 'DEFAULT',
    };
    const symbol = this.route.snapshot.paramMap.get('symbol');
    this.coin = this.findCoinBySymbol(symbol);
  }

  ngOnInit(): void {
    if (!this.isDefault(this.coin)) {
      this.getCoinData(this.coin);
    }
  }

  findCoinBySymbol(symbol: string | null) {
    const coin = this.coins.find((coin) => coin.symbol === symbol);
    if (!coin) {
      return this.defaultCoin;
    }
    return coin;
  }

  isDefault(coin: Coin) {
    return coin.name === 'DEFAULT' && coin.symbol === 'DEFAULT';
  }

  getCoinData(coin: Coin) {
    this.http.get<CoinDataResponse>(coin.link).subscribe((coin) => {
      this.coinData = coin.data.rates;
    });
  }
}

链接来自的硬币对象:

export interface Coin {
  name: string;
  symbol: string;
  link: string;
}

const coins: Coin[] = [
  {
    name: 'Bitcoin',
    symbol: 'BTC',
    link: 'https://api.coinbase.com/v2/exchange-rates?currency=BTC',
  },
  {
    name: 'Ethereum',
    symbol: 'ETH',
    link: 'https://api.coinbase.com/v2/exchange-rates?currency=ETH',
  },
  {
    name: 'Chainlink',
    symbol: 'LINK',
    link: 'https://api.coinbase.com/v2/exchange-rates?currency=LINK',
  },
];

export default coins;

CustomHttpClient代码(主要来自Angular SSR With HttpClient rehydration文章):

import { Injectable, Inject, PLATFORM_ID } from '@angular/core';
import { HttpParams, HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { tap } from 'rxjs/operators';
import {
  StateKey,
  makeStateKey,
  TransferState,
} from '@angular/platform-browser';
import { isPlatformServer } from '@angular/common';

@Injectable()
export class CustomHttpClientService {
  constructor(
    private httpClient: HttpClient,
    private transferState: TransferState,
    @Inject(PLATFORM_ID) private platformId: Object
  ) {}

  get<T>(path: string, params?: HttpParams): Observable<T> {
    const transferKey: StateKey<T> = makeStateKey(
      `${path}?${params != null ? params.toString() : ''}`
    );

    if (this.transferState.hasKey(transferKey)) {
      return of(this.transferState.get<any>(transferKey, 0)).pipe(
        tap(() => this.transferState.remove(transferKey))
      );
    } else {
      return this.httpClient
        .get<T>(path, { observe: 'body', responseType: 'json', params: params })
        .pipe(
          tap((response) => {
            if (isPlatformServer(this.platformId)) {
              this.transferState.set<T>(transferKey, response);
            }
          })
        );
    }
  }
}

核心模块:

import { NgModule } from '@angular/core';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { CustomHttpClientService } from 'src/app/core/custom-http-client.service';
import { CookiesInterceptor } from 'src/app/core/cookies.interceptor';

@NgModule({
  imports: [HttpClientModule],
  providers: [
    CustomHttpClientService,
    {
      provide: HTTP_INTERCEPTORS,
      useClass: CookiesInterceptor,
      multi: true,
    },
  ],
})
export class CoreModule {}

【问题讨论】:

  • 您介意分享CustomHttpClientService的代码吗?
  • @PsyGik 我更新了代码以包含CustomHttpClientService 和与之配套的CoreModule 的sn-ps。
  • CORS 主要是后端问题。最有可能发生的是 api.coinbase.com 不允许通过您的 localhost:4200 访问您基本上可以使用 http-proxy-middleware 代理请求或从 counbase 端仔细检查 CORS 设置

标签: angular cors server-side-rendering angular-universal


【解决方案1】:

您需要设置自己的 API(或代理)来向 Coinbase 发出请求,因为他们的 CORS 政策会阻止来自浏览器的直接请求。

刷新页面时没有出现错误的原因是它使用了 SSR,这意味着对 Coinbase 的请求实际上是从您的服务器(而不是浏览器)完成的,不受 CORS 限制的影响

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 2018-02-02
    • 1970-01-01
    • 2016-09-06
    • 2017-10-06
    • 1970-01-01
    相关资源
    最近更新 更多