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

Angular中HttpClient请求被调用两次问题求助

问题:Angular调用API时出现两次请求(OPTIONS + POST)如何解决?

我在Angular项目里尝试调用API,但发现每次都会触发两次请求:第一次是OPTIONS方法,第二次才是POST。以下是我的代码:

MyComponent.ts

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { ActivatedRoute } from '@angular/router';
import { Api } from '../../services/Api';
import { Account } from '../../models/Account';
@Component({
  selector: 'app-account-individual',
  templateUrl: './account-individual.component.html',
  styleUrls: ['./account-individual.component.css']
})
export class AccountIndividualComponent implements OnInit {
  account_id: number;
  account: Account = new Account();
  constructor(private route: ActivatedRoute, private proxy: Api) {
    this.proxy.postClient('api-get?call=get-account', { "id": 1 }).subscribe(
      res => { console.log(res); }
    );
  }
  ngOnInit() {
    this.route.params.subscribe(params => {
      this.account_id = params['id'];
    });
  }
}

Api.ts

import { Injectable } from '@angular/core';
import { Http, Headers, Response, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';
import * as Globals from 'Globals';
import { HttpClient } from '@angular/common/http';
@Injectable()
export class Api {
  api_url = "http://localhost/MyProject/";
  constructor(private http: Http, private httpClient: HttpClient) { }
  fetchData(url) {
    return this.http.get(this.api_url + url).map(this.extractData).catch(this.handleErrorPromise);
  }
  postClient(url: string, data?: any){
    return this.httpClient.post(this.api_url + url, data);
  }
  private extractData(res: Response) {
    let body = res.json();
    return body||{};
  }
  private handleErrorObservable(error: Response | any) {
    console.error(error.message || error);
    return Observable.throw(error.message || error);
  }
  private handleErrorPromise(error: Response | any) {
    console.error(error.message || error);
    return Promise.reject(error.message || error);
  }
}

服务端CORS处理代码(PHP)

public function actionApiGet($call){
  header('Access-Control-Allow-Origin: *');
  header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
  header('Access-Control-Allow-Headers: x-test-header, Origin, X-Requested-With, Content-Type, Accept');
  $data = json_decode(file_get_contents('php://input'), true);
  ...
}

现在想知道怎么解决这种两次请求的问题?


解答

先给你理清楚哈:这个OPTIONS请求不是代码出bug了,它是浏览器自动发起的预请求(Preflight Request),属于CORS跨域规范里的正常操作。当你发起的跨域请求是非简单请求时(比如像你这样用POST发送JSON数据,Content-Type是application/json),浏览器会先给服务器发个OPTIONS请求,确认服务器允许这个跨域请求后,才会发送真正的POST请求。

不过我们可以通过优化服务器端的处理,避免不必要的资源消耗,同时让请求更高效:

  • 单独拦截OPTIONS请求,直接返回成功响应
    你现在的服务端代码会在处理业务逻辑的同一个方法里处理OPTIONS请求,可能会白白执行后续的解析请求体、处理业务的代码,完全没必要。你可以先判断请求方法,如果是OPTIONS,直接返回200状态码然后退出,不执行后续逻辑:

    public function actionApiGet($call){
      // 优先处理OPTIONS预请求
      if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
        header('Access-Control-Allow-Headers: x-test-header, Origin, X-Requested-With, Content-Type, Accept');
        http_response_code(200);
        exit; // 直接退出,跳过后续业务逻辑
      }
    
      // 处理实际的POST/GET业务请求
      header('Access-Control-Allow-Origin: *');
      $data = json_decode(file_get_contents('php://input'), true);
      // ... 你的业务代码
    }
    
  • 给预请求添加缓存,减少重复OPTIONS请求
    你可以在OPTIONS的响应头里加上Access-Control-Max-Age,指定预请求结果的缓存时长(单位是秒)。这样浏览器在缓存有效期内,访问同一个接口时就不会重复发OPTIONS请求了:

    if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
      header('Access-Control-Allow-Origin: *');
      header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
      header('Access-Control-Allow-Headers: x-test-header, Origin, X-Requested-With, Content-Type, Accept');
      header('Access-Control-Max-Age: 86400'); // 缓存1天,可根据需求调整时长
      http_response_code(200);
      exit;
    }
    
  • 看看能不能转为简单请求(可选)
    如果你的请求能符合"简单请求"的标准,浏览器就不会发预请求。简单请求的要求是:

    • 请求方法只能是GET、HEAD或POST
    • 请求头只能包含Accept、Accept-Language、Content-Language、Content-Type(且值只能是application/x-www-form-urlencoded、multipart/form-data或text/plain)
      不过你的请求是发送JSON数据,Content-Type是application/json,不符合简单请求的要求,所以这种情况下OPTIONS预请求是没法避免的,只能用前面两种方法优化。

总的来说,OPTIONS请求是跨域非简单请求的正常流程,我们没法完全消除它,但通过服务器端的优化,可以让它不影响业务逻辑,同时减少重复请求的次数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:55:57