如何使用angular2和php获取数据时解决Cross-Origin-Request-Block问题?

时间:2018-04-03 08:23:46

标签: php angular cors angular2-services

请详细阅读该问题,因为它是一个很长的问题,根据其他用户的要求进行了各种编辑和扩展更新。

我正在尝试使用angular2将数据发送到php文件。我在Unix中进行角度项目,var/www/html/是用于运行php文件的php Xampp文件夹位置。

我的文件夹结构如下: -

var/www/html/
        |_(angproject)
                |_(phpscript)
                |       |_login.php
                |_(src)
                   |_(app)
                        |_(admin)
                        |   |_(login)
                        |   |   |_login.component.ts
                        |   |
                        |   |_admin.component.ts
                        |
                        |_(_admin_service)
                        |       |_admin.login.ts
                        |
                        |_(_animations)
                        |
                        |_(front)
                        |
                        |_(_models)
                        |   |_admin.model.ts
                        |
                        |_app.module.ts

我的 app.module.ts 文件是这样的: -

import { HttpModule, Http, Response, Headers, RequestOptions} from '@angular/http';
import { HttpClientModule } from '@angular/common/http';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { APP_BASE_HREF } from '@angular/common';
import { CanActivate } from "@angular/router";


import { AppComponent } from './app.component';
import { FrontComponent } from './front/front.component';
import { AdminComponent } from './admin/admin.component';
import { LoginComponent } from './admin/login/login.component';
import { DashboardComponent } from './admin/dashboard/dashboard.component';
import { HeaderComponent } from './admin/header/header.component';
import { FooterComponent } from './admin/footer/footer.component';
import { LeftmenuComponent } from './admin/leftmenu/leftmenu.component';
import { NavbarComponent } from './admin/navbar/navbar.component';
import { ShortcutbarComponent } from './admin/shortcutbar/shortcutbar.component';
import { AdminLoginService } from './_admin_service/admin.login';

const appRoutes: Routes = [
  { path: 'admin',
    component: AdminComponent,
    children: [
          { path: '', component: LoginComponent},
          { path: 'dashboard', component: DashboardComponent}
    ]
  }
];

@NgModule({
  declarations: [
    AppComponent,
    FrontComponent,
    AdminComponent,
    LoginComponent,
    DashboardComponent,
    HeaderComponent,
    FooterComponent,
    LeftmenuComponent,
    NavbarComponent,
    ShortcutbarComponent
  ],
  imports: [
    HttpModule,
    HttpClientModule,
    BrowserModule,
    BrowserAnimationsModule,
    FormsModule,
    RouterModule.forRoot(
      appRoutes,
      { enableTracing: true } // <-- debugging purposes only
    )
  ],
  providers: [{provide: APP_BASE_HREF, useValue : '/' },AdminLoginService],
  bootstrap: [AppComponent]
})
export class AppModule { }

我的 login.component.ts 文件是这样的: -

import { Component, OnInit, Input } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';

import { fadeInAnimation } from '../../_animations/index';
import { Admin } from '../../_models/admin.model';
import { AdminLoginService } from '../../_admin_service/admin.login';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css'],
  animations: [fadeInAnimation],
  host: { '[@fadeInAnimation]': '' },
  providers: [AdminLoginService]
})

export class LoginComponent implements OnInit {
    loading = false;
    returnUrl: string;
    responseStatus:Object= [];
    status:boolean ;
    //@Input() admin:Admin;

   model = new Admin('', '', '', 'Emailsss','Passwordsss'); 

    constructor(
        private route: ActivatedRoute,
        private router: Router,
        private _adminLogin: AdminLoginService
    ){}

  submitPost()
  {        
      //console.log("submit Post click happend " + this.model.email)
      //console.log(this.model);
      this._adminLogin.postLogin(this.model).subscribe(
         data => console.log(this.responseStatus = data),
         err => console.log(err),
         () => console.log('Request Completed')
      ); 
      this.status = true;       
  }

    ngOnInit() {

    }
}

服务文件 admin.login.ts 文件是: -

import { Http, Response, Headers, RequestOptions} from '@angular/http';
import { HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import 'rxjs/add/operator/map';
import { Admin } from '../_models/admin.model';

@Injectable()
export class AdminLoginService {
    http : Http;
    actionUrl : string;
    admin_login_Url: string;
    postData: Admin;


    constructor(public _http: Http) {
       this.http = _http;
       this.admin_login_Url = 'http://localhost/angproject/phpscript/login.php';
    }
    postLogin(postData:Admin) {
        let headers = new Headers();
        headers.append("Access-Control-Allow-Origin","*");
        headers.append("Access-Control-Allow-Methods","GET, POST");
        headers.append("Content-Type","application/json");

        let options = new RequestOptions({ headers: headers });
        console.log(postData);

        this.actionUrl = this.admin_login_Url;
        return this.http.post(this.actionUrl, {postData}, options)
          .map(res => res.json());          
    }
 }

最后,我的login.php文件是: -

<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST");
header("Access-Control-Allow-Headers: *");
header("Content-Type: application/json; charset=utf-8");
include('connection.php');

$rawData    = file_get_contents("php://input");
$data       = json_decode($rawData, true);

$error = array();
if(isset($data['postData']['email']) && !empty($data['postData']['email']))
    $email = $data['postData']['email'];
else 
    $error[] = "Email was not entered";
if(isset($data['postData']['password']) && !empty($data['postData']['password']))
    $password = $data['postData']['password'];
else 
    $error[] = "Password was not entered";

if(empty($error))
{
    $runQuery = "SELECT * FROM users WHERE email = '$email' AND password = '$password'";
    $result = $conn->query($runQuery);
    if ($result->num_rows > 0) 
    {
        $response['status'] = 1;
        $response['message'] = "Login successfully";
        $response['error'] = 0;
    } 
    else 
    {
        $response['status'] = 0;
        $response['message'] = "An error occured while logging in";
        $response['error'] = $conn->error;
    }
}
else
{
    $response['status'] = 0;
    $response['message'] = "Parameter missing";
    $response['error'] = $error;
}
$respond = json_encode($response);
echo $respond;
exit;
?>

现在问题就在于此。在Chrome中进行测试时,单击提交按钮一次就会激活两次调用php脚本(Ajax)。第一个调用不发送任何数据,因此它在返回响应中显示验证消息。第二个调用发送表单的数据,因此通过匹配发送的数据来获取所需的结果。

enter image description here

在firefox的情况下,我收到了这样的回复: -

enter image description here

enter image description here

我该如何解决?

注意

以下是&#34; 第一次通话&#34;的请求标题在chrome:

enter image description here enter image description here

以下是&#34; 第二次调用&#34;的请求标头在铬: enter image description here enter image description here

编辑:日期 - 04-04-2018

根据 David 的建议,我在login.php文件中进行了后续更改:

header("Access-Control-Allow-Origin: http://localhost:4200");
header("Access-Control-Allow-Headers: application/x-www-form-urlencoded");
header("Access-Control-Allow-Methods: POST, GET, OPTIONS");

在admin.login.ts文件中,我进行了以下更改: -

postLogin(postData:Admin) {
  let headers = new Headers();
  headers.append("Accept", "q=0.8;application/json;q=0.9");
  this.actionUrl = this.admin_login_Url;
  return this.http.post(this.actionUrl, {postData}, { headers: headers })
          .map(res => res.json()).share();
    }
}

问题1: 对于上面的代码,我参考了这个site。然而,我仍然得到错误。如何在Firefox中修复此问题?

在上面的代码中,我添加了行headers.append("Accept", "q=0.8;application/json;q=0.9");来克服下面显示的错误: -

enter image description here

我该如何克服这个问题?

问题2:

在Firefox中,控制台显示:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost/angproject/phpscript/register.php. (Reason: invalid token ‘application/x-www-form-urlencoded’ in CORS header ‘Access-Control-Allow-Headers’).

enter image description here

在Chrome中查看时,我可以在控制台的网络标签中看到两个电话。第一个呼叫的请求类型是OPTIONS,而第二个呼叫的请求类型是POST。

对于Firefox,我只能通过请求类型OPTIONS进行一次调用。 POST请求根本没有发生。

enter image description here

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:3)

由于CORS实施(localhost:4200上的客户端,localhost上的服务器=不同的端口),有2个请求是正常的。

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

您的问题是您在请求中指定了一些CORS标头

headers.append("Access-Control-Allow-Methods","GET, POST");
headers.append("Access-Control-Allow-Origin","*");

这些标题旨在添加服务器端,您无需从角度发送它们。他们实际上是造成了这个问题。

删除它们,它应该可以工作。

有关 Access-Control-Allow-Headers

的详细信息

Access-Control-Allow-Headers通常会接受以逗号分隔的标头列表。 https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers

所有浏览器都不支持PHP代码中的通配符(*)值。在Firefox上,尚未实现通配符 https://bugzilla.mozilla.org/show_bug.cgi?id=1309358

这就是为什么firefox会让你的POST失败,因为预检(OPTIONS)请求失败了

您无需在列表中指定Content-Type,因为默认情况下已接受

  

简单标题,接受,接受语言,内容语言,内容类型(但只有其应用程序/ x-www-form-urlencoded,multipart / form的解析值的MIME类型(忽略参数) -data或text / plain)始终可用,不需要通过此标题列出。

修改:实际上,您需要在发送application/json之前在接受的标头列表中指定内容类型,这不在上面的列表中

答案 1 :(得分:0)

在Windows的chrome浏览器中运行角度应用程序时,我遇到了同样的问题。甚至我在后端解决问题时也没有设置CORS配置。然后,我意识到我需要在本地计算机或

上运行时禁用网络安全性
  

localhost / portNumber

只需在cmd上单击以下命令

TASKKILL /F /IM chrome.exe
start chrome.exe --args --disable-web-security –-allow-file-access-from-files

这解决了我的问题:)