headers example node.js angularjs express cors ngresource

node.js - headers - httpclient angular 5 example



El recurso AngularJS $ hace la solicitud HTTP OPTIONS en lugar de HTTP POST para el método $ save (2)

Realmente no intenté esto, pero ¿no sería suficiente decirle al Ressource cómo manejar la solicitud $ save?

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

Estoy en el proceso de escribir una aplicación de biblioteca simple para prepararme para un proyecto más grande con AngularJS. Después de leer mucho en línea sobre el uso de $resource para interactuar con una API RESTful, decidí que probablemente ofrecería algunos beneficios de escalamiento y ahorro de tiempo para implementarlo en lugar de usar $http para cada solicitud. El problema es que por alguna razón (no soy experto en CORS y la solicitud se envía a través del dominio) cuando uso el método $save que muestra mi consola Node.js:

OPTIONS /books 200 1ms - 161b

El uso del método query() funciona bien: la consola del nodo muestra:

GET /books 200 1ms - 228b

Estuve atascado durante varias horas en este punto, probando variaciones en el siguiente, pero siempre termina siendo una solicitud de OPCIONES en lugar de POST (que es lo que debería ser de acuerdo con la documentación de Angular) para el método $save .

Aplicación web 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 con 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); };

Intenté poner esta línea en mi función de configuración delete $httpProvider.defaults.headers.common["X-Requested-With"]; pero no hay cambio

No soy un profesional de Angular / Node pero en este momento estoy pensando que tiene algo que ver con el dominio cruzado y, como dije, no soy un experto en CORS.

Gracias por adelantado.


Sé que puede ser de mal gusto responder a mi propia pregunta, pero descubrí el problema unos días después de publicar esto.

Todo se reduce a cómo los navegadores manejan CORS. Al realizar una solicitud entre dominios en JavaScript que no es "simple" (es decir, una solicitud GET, que explica por qué funcionó la función query() ), el navegador realizará automáticamente una solicitud HTTP OPTIONS al URL / URI especificado, llamado solicitud "previa al vuelo" o "promesa". Siempre que la fuente remota devuelva un código de estado HTTP de 200 y detalles relevantes sobre lo que aceptará en los encabezados de respuesta, el navegador continuará con la llamada original de JavaScript.

Aquí hay un breve ejemplo de 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''s that simple. }

Todo lo que tuve que hacer fue agregar los detalles de lo que el servidor aceptará en los encabezados de respuesta:

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

Y luego inserte estas pocas líneas antes del enrutamiento Express para simplemente devolver un código de estado HTTP 200 para cada solicitud de OPCIONES:

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

Espero que esto ayude a cualquiera que tropiece en esta página que sufre del mismo problema.