请详细阅读该问题,因为它是一个很长的问题,根据其他用户的要求进行了各种编辑和扩展更新。
我正在尝试使用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)。第一个调用不发送任何数据,因此它在返回响应中显示验证消息。第二个调用发送表单的数据,因此通过匹配发送的数据来获取所需的结果。
在Firefox的情况下,我得到这样的回应: -
我该如何解决?
注意:
以下是chrome中“第一次调用”的请求标头:
编辑:日期 - 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");
来克服如下所示的错误: -
我怎样才能克服这个问题?
问题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请求根本没有发生。
我怎样才能解决这个问题?
由于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-data的解析值(忽略参数)的MIME类型,或text / plain),始终可用,不需要通过此标题列出。
编辑:实际上,您需要在接受的标题列表中指定内容类型,因为您要发送application/json
,这不在上面的列表中
我在Windows中的chrome浏览器上运行角度应用程序时遇到了同样的问题。即使我在后端设置CORS配置,问题也没解决。然后我意识到我需要在本地计算机上运行时禁用Web安全性
本地主机/端口号
只需在cmd上点击以下命令即可
TASKKILL /F /IM chrome.exe
start chrome.exe --args --disable-web-security –-allow-file-access-from-files
这解决了我的问题:)