【问题标题】:AngularJS POST Fails: Response for preflight has invalid HTTP status code 404AngularJS POST 失败:预检响应具有无效的 HTTP 状态代码 404
【发布时间】:2016-02-13 03:27:19
【问题描述】:

我知道有很多这样的问题,但我所见过的都没有解决我的问题。我已经使用了至少 3 个微框架。他们都在做一个简单的 POST 时失败了,这应该返回数据:

angularJS 客户端:

var app = angular.module('client', []);

app.config(function ($httpProvider) {
  //uncommenting the following line makes GET requests fail as well
  //$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 服务器:

<?php
    require 'vendor/autoload.php';

    $app = new \Slim\Slim();
    $app->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 内容进行更新。但是我得到了一个

XMLHttpRequest 无法加载 http://localhost:8080/server.php/post。预检响应包含无效的 HTTP 状态代码 404

每次我尝试使用 POST。为什么?

编辑:Pointy 要求的 req/res

【问题讨论】:

  • 那么预检 HTTP 请求/响应是什么样的?
  • 嗯,“OPTIONS”请求不需要明确的路由吗?您只有“GET”和“POST”的路由。
  • 显然,当我尝试执行 POST 时,它会强制执行 OPTIONS 请求。它不应该与 POS T 一起使用吗?我有义务处理 OPTIONS 吗?为什么?
  • 当 POST 请求具有某些特征时,浏览器会首先执行“预检”OPTIONS 事务。 POST 必须“简单”以避免它 - 这意味着它必须使用 application/x-www-form-urlencodedmultipart/form-datatext/plainContent-Type,并且它不能有任何自定义标题。我不确定您的 POST 究竟是什么触发了预检测试。
  • 可能是因为我正在发送一个 JSON 对象。感谢您的输入,一旦我回到代码中,我会尝试实现它

标签: javascript php angularjs ajax cors


【解决方案1】:

编辑:

已经好几年了,但我觉得有必要进一步评论一下。现在我实际上是一名开发人员。对您的后端的请求通常使用您的框架将获取和处理的令牌进行身份验证;这就是缺少的。我实际上根本不确定这个解决方案是如何工作的。

原文:

好的,这就是我的想法。 这一切都与 CORS 政策有关。在 POST 请求之前,Chrome 正在执行预检 OPTIONS 请求,该请求应在实际请求之前由服务器处理和确认。现在,对于这样一个简单的服务器,这真的不是我想要的。因此,重置标头客户端会阻止预检:

app.config(function ($httpProvider) {
  $httpProvider.defaults.headers.common = {};
  $httpProvider.defaults.headers.post = {};
  $httpProvider.defaults.headers.put = {};
  $httpProvider.defaults.headers.patch = {};
});

浏览器现在将直接发送 POST。希望这可以帮助很多人......我真正的问题是对 CORS 的了解不够。

链接到一个很好的解释:http://www.html5rocks.com/en/tutorials/cors/

感谢this answer 为我指路。

【讨论】:

  • 由于这获得了很多浏览量,我应该提一下,这不是您想要的生产应用程序。您应该相应地处理预检。
  • 这对我来说没有任何改变......不明白为什么或应该做什么
  • 别忘了添加 $httpProvider.defaults.headers.get = {};如果您正在执行 $http.get() 请求。
  • @AlexOlival 惊人的输入,你愿意与大家分享为什么这不是你应该做的,也是你应该做的
  • @AlexOlival 应该如何在生产中更准确地处理?据我了解,服务器应该允许对所有请求使用 OPTIONS 方法,对吗?
【解决方案2】:

对于 Node.js 应用程序,在注册我自己的所有路由之前的 server.js 文件中,我将代码放在下面。它为所有响应设置标题。如果它是飞行前的“OPTIONS”调用,它也会优雅地结束响应,并立即将飞行前的响应发送回客户端,而无需通过实际的业务逻辑路由“下一步”(这是一个词吗?)。这是我的 server.js 文件。突出显示供 Stackoverflow 使用的相关部分。

// server.js

// ==================
// BASE SETUP

// import the packages we need
var express    = require('express');
var app        = express();
var bodyParser = require('body-parser');
var morgan     = require('morgan');
var jwt        = require('jsonwebtoken'); // used to create, sign, and verify tokens

// ====================================================
// configure app to use bodyParser()
// this will let us get the data from a POST
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

// Logger
app.use(morgan('dev'));

// -------------------------------------------------------------
// STACKOVERFLOW -- PAY ATTENTION TO THIS NEXT SECTION !!!!!
// -------------------------------------------------------------

//Set CORS header and intercept "OPTIONS" preflight call from AngularJS
var allowCrossDomain = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
    res.header('Access-Control-Allow-Headers', 'Content-Type');
    if (req.method === "OPTIONS") 
        res.send(200);
    else 
        next();
}

// -------------------------------------------------------------
// STACKOVERFLOW -- END OF THIS SECTION, ONE MORE SECTION BELOW
// -------------------------------------------------------------


// =================================================
// ROUTES FOR OUR API

var route1 = require("./routes/route1");
var route2 = require("./routes/route2");
var error404 = require("./routes/error404");


// ======================================================
// REGISTER OUR ROUTES with app

// -------------------------------------------------------------
// STACKOVERFLOW -- PAY ATTENTION TO THIS NEXT SECTION !!!!!
// -------------------------------------------------------------

app.use(allowCrossDomain);

// -------------------------------------------------------------
//  STACKOVERFLOW -- OK THAT IS THE LAST THING.
// -------------------------------------------------------------

app.use("/api/v1/route1/", route1);
app.use("/api/v1/route2/", route2);
app.use('/', error404);

// =================
// START THE SERVER

var port = process.env.PORT || 8080;        // set our port
app.listen(port);
console.log('API Active on port ' + port);

【讨论】:

    【解决方案3】:

    您已启用 CORS 并在服务器中启用了 Access-Control-Allow-Origin : *。如果您仍然得到 GET 方法工作而 POST 方法不工作,则可能是因为 Content-Typedata 问题的问题.

    首先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&amp;Password=12345 形式的POST 数据。

    【讨论】:

      猜你喜欢
      • 2017-05-06
      • 1970-01-01
      • 2017-09-26
      • 2018-03-12
      • 2018-04-25
      • 2016-10-10
      • 2016-01-22
      • 2016-05-21
      • 2017-12-22
      相关资源
      最近更新 更多