AngularJS POST 请求失败:Response for preflight has invalid HTTP status code 404
AngularJS POST 请求失败:Response for preflight has invalid HTTP status code 404
我知道有很多类似的问题,但是我看到的没有一个解决我的问题。我已经使用了至少3个微框架,它们都无法执行简单的POST操作,而这个操作应该将数据返回:
AngularJS客户端:
var app = angular.module('client', []); app.config(function ($httpProvider) { //取消下面一行的注释也会导致GET请求失败 //$httpProvider.defaults.headers.common['Access-Control-Allow-Headers'] = '*'; delete $httpProvider.defaults.headers.common['X-Requested-With']; }); app.controller('MainCtrl', function($scope, $http) { var baseUrl = 'http://localhost:8080/server.php' $scope.response = 'Response goes here'; $scope.sendRequest = function() { $http({ method: 'GET', url: baseUrl + '/get' }).then(function successCallback(response) { $scope.response = response.data.response; }, function errorCallback(response) { }); }; $scope.sendPost = function() { $http.post(baseUrl + '/post', {post: 'data from client', withCredentials: true }) .success(function(data, status, headers, config) { console.log(status); }) .error(function(data, status, headers, config) { console.log('FAILED'); }); } });
SlimPHP服务器:
response()->headers->set('Access-Control-Allow-Headers', 'Content-Type'); $app->response()->headers->set('Content-Type', 'application/json'); $app->response()->headers->set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); $app->response()->headers->set('Access-Control-Allow-Origin', '*'); $array = ["response" => "Hello World!"]; $app->get('/get', function() use($array) { $app = \Slim\Slim::getInstance(); $app->response->setStatus(200); echo json_encode($array); }); $app->post('/post', function() { $app = \Slim\Slim::getInstance(); $allPostVars = $app->request->post(); $dataFromClient = $allPostVars['post']; $app->response->setStatus(200); echo json_encode($dataFromClient); }); $app->run();
我已经启用了CORS,GET请求可以正常工作。HTML更新为服务器发送的JSON内容。但是每次我尝试使用POST时,我都会得到一个XMLHttpRequest无法加载http://localhost:8080/server.php/post。预检请求的响应具有无效的HTTP状态码404。为什么?
编辑:如Pointy所请求的那样,以下是请求/响应的头部信息:
问题的原因可能是由于服务器端启用了CORS并且在服务器端启用了Access-Control-Allow-Origin : *。如果GET方法可以正常工作,但POST方法无法正常工作,那么可能是由于Content-Type和data的问题。
AngularJS使用Content-Type: application/json传输数据,但某些Web服务器(尤其是PHP)不能原生地序列化它们。对于这些服务器,我们必须将数据传输为Content-Type: x-www-form-urlencoded。
示例代码如下:
$scope.formLoginPost = function () { $http({ url: url, method: "POST", data: $.param({ 'username': $scope.username, 'Password': $scope.Password }), headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).then(function (response) { // success console.log('success'); console.log("then : " + JSON.stringify(response)); }, function (response) { // optional // failed console.log('failed'); console.log(JSON.stringify(response)); }); };
注意:我使用$.params来将数据序列化为Content-Type: x-www-form-urlencoded。或者您可以使用以下JavaScript函数:
function params(obj){ var str = ""; for (var key in obj) { if (str != "") { str += "&"; } str += key + "=" + encodeURIComponent(obj[key]); } return str; }
并使用params({ 'username': $scope.username, 'Password': $scope.Password })将其序列化为Content-Type: x-www-form-urlencoded请求,该请求仅以username=john&Password=12345的形式获取POST数据。
问题原因:
出现这个问题的原因是因为在AngularJS的POST请求中,浏览器会先发送一个预检请求(preflight request)来检查服务器是否支持该请求。预检请求使用OPTIONS方法,服务器需要正确处理该请求并返回正确的响应。
在给出的代码中,服务器在处理预检请求时没有正确设置响应头,导致预检请求返回了一个错误的响应状态码404。
解决方法:
为了解决这个问题,需要在服务器端正确设置响应头来处理预检请求。在给出的代码中,可以在allowCrossDomain函数中添加以下代码来处理OPTIONS请求:
if (req.method === "OPTIONS") { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE'); res.header('Access-Control-Allow-Headers', 'Content-Type'); res.sendStatus(200); } else { next(); }
这样就能正确处理预检请求,并返回正确的响应状态码200。这样AngularJS的POST请求就能正常发送和接收数据了。
问题原因:该问题是由于CORS策略引起的。在进行POST请求之前,Chrome会发送一个预检OPTIONS请求,服务器在实际请求之前需要处理和确认该请求。然而,对于一个简单的服务器来说,这并不是我想要的结果。因此,通过在客户端重置headers来阻止预检请求。
解决方法:在app.config中添加以下代码,重置headers以阻止预检请求:
app.config(function ($httpProvider) { $httpProvider.defaults.headers.common = {}; $httpProvider.defaults.headers.post = {}; $httpProvider.defaults.headers.put = {}; $httpProvider.defaults.headers.patch = {}; });
这样浏览器就会直接发送POST请求。
注意事项:这种解决方法并不适用于生产环境,应该根据需要处理预检请求。