Как решить проблему Cross-Origin-Request-Block при получении данных с использованием angular2 и php?

Пожалуйста, прочитайте вопрос подробно, так как он длинный с различными правками и расширенными обновлениями по запросу других пользователей.

Я пытаюсь отправить данные в php файл с помощью angular2. Я делаю угловой проект в Unix и var/www/html/ расположение папок php Xampp для запуска файлов php

Моя структура папок выглядит так:

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) дважды. Первый вызов не отправляет никаких данных и, следовательно, показывает ответное сообщение в ответном ответе. Второй вызов отправляет данные формы и, следовательно, выбирает желаемый результат путем сопоставления отправленных данных.

введите описание изображения здесь

В случае Firefox, я получаю этот ответ: —

введите описание изображения здесь

введите описание изображения здесь

Как я могу решить это?

Заметка:

Вот заголовки запроса «первый звонок«в хроме:

введите описание изображения здесь
введите описание изображения здесь

Вот заголовки запроса «второй звонок«в хроме:
введите описание изображения здесь
введите описание изображения здесь

РЕДАКТИРОВАТЬ: от 04-04-2018:

Согласно предложениям от Дэвид, Я сделал последующие изменения в моем файле 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:
Для приведенного выше кода, я взял ссылку из этого сайт. Тем не менее, я все еще получаю ошибку. Как я могу это исправить в Firefox?

В приведенном выше коде я добавил строку headers.append("Accept", "q=0.8;application/json;q=0.9"); чтобы преодолеть ошибку, показанную ниже:

введите описание изображения здесь

Как я могу преодолеть эту проблему?

Вопрос 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’).

введите описание изображения здесь

При проверке в Chrome, я вижу два вызова на вкладке сети в консоли. Тип запроса первого вызова — OPTIONS, в то время как второй вызов имеет тип запроса POST.

В случае Firefox я получаю только один звонок с запросом типа OPTIONS. Запрос POST не выполняется вообще.

введите описание изображения здесь

Как я могу это исправить?

0

Решение

Наличие 2 запросов — это нормально, из-за реализации CORS (клиент включен localhost:4200сервер на localhost = разные порты).

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","*");

Эти заголовки предназначены для добавления на стороне сервера, вам не нужно отправлять их из angular. Они на самом деле вызывают проблемы.

Удалите их, и это должно работать.

Больше информации о 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, так как запрос preflight (OPTIONS) не удается

Вам не нужно указывать Content-Type в вашем списке, так как он принят по умолчанию

Простые заголовки, Accept, Accept-Language, Content-Language, Content-Type (но только с MIME-типом его проанализированного значения (игнорирующие параметры) либо application / x-www-form-urlencoded, multipart / form-data, или text / plain), всегда доступны и не должны быть перечислены в этом заголовке.

редактировать: на самом деле, вам нужно указать тип содержимого в списке принятых заголовков, так как вы отправляете application/json, которого нет в списке выше

2

Другие решения

Других решений пока нет …