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.