Подтвердить что ты не робот

Ресурс AngularJS $делает запрос HTTP OPTIONS вместо HTTP POST для метода $save

Я собираюсь написать простое библиотечное приложение, чтобы подготовиться к более крупному проекту с помощью AngularJS. Прочитав много онлайн об использовании $resource для взаимодействия с RESTful API, я решил, что он, вероятно, предложит некоторые преимущества экономии времени и масштабирования для его реализации вместо использования $http для каждого запроса. Проблема заключается в том, что по какой-то причине (я не эксперт по CORS и запрос отправляется в кросс-домен) при использовании метода $save моя консоль Node.js показывает:

OPTIONS /books 200 1ms - 161b 

Использование метода query() отлично работает - консоль Node показывает:

GET /books 200 1ms - 228b

Я застрял в течение нескольких часов на этом этапе, пытаясь изменить варианты ниже, но он всегда заканчивается тем, что это запрос OPTIONS вместо POST (что должно быть в соответствии с документацией Angular) для $save.

Веб-приложение AngularJS

app.js

var libraryApp = angular.module('libraryApp', ['ngResource', 'ngRoute', 'libraryControllers']);

libraryApp.factory('$book', ['$resource', function ($resource) {

    return $resource('http://mywebserver\\:1337/books/:bookId', { bookId: '@bookId' });
}]);

controllers.js

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

libraryControllers.controller('BookCtrl', ['$scope', '$book', function($scope, $book) {

    ...

    $scope.addBook = function () {
        var b = new $book;
        b.isbn = "TEST";
        b.description = "TEST";
        b.price = 9.99;
        b.$save();
    };
}]);

Node.js с API Express REST

app.js

var express = require('express'),
    books = require('./routes/books'),
    http = require('http'),
    path = require('path');

var app = express();

...

// enable cross-domain scripting
app.all('*', function(req, res, next) {
    res.header("Access-Control-Allow-Origin", req.headers.origin);
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    next();
});

// routing
app.get('/books', books.getAll);
app.get('/books/:isbn', books.get);
// This is what I want to fire with the $save method
app.post('/books', books.add);

http.createServer(app).listen(app.get('port'), function(){
    console.log('Express server listening on port ' + app.get('port'));
});

./маршруты/books.js

...

exports.add = function(req, res) {

    console.log("POST request received...");
    console.log(req.body.isbn);
};

Пробовал поместить эту строку в мою конфигурационную функцию delete $httpProvider.defaults.headers.common["X-Requested-With"];, но без изменений.

Я не Angular/Node pro, но сейчас я думаю, что это связано с тем, что это кросс-домен, и, как я уже сказал, я не эксперт в CORS.

Спасибо заранее.

4b9b3361

Ответ 1

Я знаю, что может быть плохой ответ, чтобы ответить на мой собственный вопрос, но я выяснил проблему через несколько дней после публикации этого сообщения.

Все сводится к тому, как браузеры управляют CORS. При выполнении междоменного запроса в JavaScript, который не является "простым" (т.е. Запрос GET - который объясняет, почему работала функция query()), браузер автоматически сделает запрос HTTP OPTIONS на указанный URL/URI, называемый "предполетный" запрос или "обещание". Пока удаленный источник возвращает код состояния HTTP 200 и соответствующие сведения о том, что он примет в заголовках ответов, браузер будет продолжать использовать JavaScript-код.

Вот пример jQuery:

function makeRequest() {
    // browser makes HTTP OPTIONS request to www.myotherwebsite.com/api/test
    // and if it receives a HTTP status code of 200 and relevant details about
    // what it will accept in HTTP headers, then it will make this POST request...
    $.post( "www.myotherwebsite.com/api/test", function(data) {
        alert(data);
    });
    // ...if not then it won't - it that simple.
}

Все, что мне нужно было сделать, это добавить сведения о том, что сервер будет принимать в заголовках ответов:

// apply this rule to all requests accessing any URL/URI
app.all('*', function(req, res, next) {
    // add details of what is allowed in HTTP request headers to the response headers
    res.header('Access-Control-Allow-Origin', req.headers.origin);
    res.header('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS');
    res.header('Access-Control-Allow-Credentials', false);
    res.header('Access-Control-Max-Age', '86400');
    res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
    // the next() function continues execution and will move onto the requested URL/URI
    next();
});

И затем вставьте эти несколько строк перед экспресс-маршрутизацией, чтобы просто вернуть код состояния HTTP 200 для каждого запроса OPTIONS:

// fulfils pre-flight/promise request
app.options('*', function(req, res) {
    res.send(200);
});

Надеюсь, это поможет любому, кто наткнулся на эту страницу, испытывая ту же проблему.

Ответ 2

Я действительно не пытался это сделать, но не было бы достаточно сказать Ressource, как обращаться с запросом $save?

$resource('http://mywebserver\\:1337/books/:bookId', { bookId: '@bookId' }, {save: {method: 'POST'});