AngularJS $resource makes HTTP OPTIONS request instead of HTTP POST for $save method

三世轮回 提交于 2019-12-08 22:53:43

问题


I'm in the process of writing a simple library application to get ready for a larger project with AngularJS. After reading a lot online about using $resource to interact with a RESTful API, I decided that it would probably offer some time-saving and scaling benefits to implement it instead of using $http for each request. The problem is that for some reason (I'm no expert on CORS and the request is being sent cross-domain) when using the $save method my Node.js console shows:

OPTIONS /books 200 1ms - 161b 

Using the query() method works fine - the Node console shows:

GET /books 200 1ms - 228b

I've been stuck for several hours at this point, trying variations on the below but it always ends up being an OPTIONS request instead of POST (which is what it should be according to the Angular documentation) for the $save method.

AngularJS Web App

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 with Express REST API

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'));
});

./routes/books.js

...

exports.add = function(req, res) {

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

Tried putting this line in my config function delete $httpProvider.defaults.headers.common["X-Requested-With"]; but no change.

I'm no Angular/Node pro but right now I'm thinking that it's something to do with it being cross domain and, like I said, I'm no expert on CORS.

Thanks in advance.


回答1:


I know it may be in bad taste to answer my own question but I figured out the problem a few days after posting this.

It all comes down to how browsers manage CORS. When making a cross-domain request in JavaScript that is not "simple" (i.e. a GET request - which explains why the query() function worked), the browser will automatically make a HTTP OPTIONS request to the specified URL/URI, called a "pre-flight" request or "promise". As long as the remote source returns a HTTP status code of 200 and relevant details about what it will accept in the response headers, then the browser will go ahead with the original JavaScript call.

Here's a brief jQuery example:

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's that simple.
}

All I had to do was add the details of what the server will accept in the response headers:

// 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();
});

And then insert these few lines before the Express routing to simply return a HTTP 200 status code for every OPTIONS request:

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

Hopefully this will help anyone who stumbles on this page suffering from the same problem.




回答2:


I didn´t actually try this, but wouldn´t it be enough to tell the Ressource how to handle the $save request?

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


来源:https://stackoverflow.com/questions/21221688/angularjs-resource-makes-http-options-request-instead-of-http-post-for-save-me

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!