When utilizing AngularJS $resource, it sends an HTTP OPTIONS request in place of the expected HTTP POST when calling the

As I work on developing a basic library application in preparation for a larger AngularJS project, I have been exploring the benefits of using $resource over $http to interact with a RESTful API. While implementing $resource seemed promising for saving time and scaling the application, I encountered an issue when trying to use the $save method which resulted in a CORS-related error:

OPTIONS /books 200 1ms - 161b 

Interestingly, the query() method worked without any problems as seen from the Node console output:

GET /books 200 1ms - 228b

Despite spending hours troubleshooting, including adjusting configurations as shown below, I could not get the $save method to trigger a POST request as expected.

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

I attempted removing the line

delete $httpProvider.defaults.headers.common["X-Requested-With"];
in my configuration function, but it did not resolve the issue.

While I am not an expert in Angular or Node, I suspect that the problem lies with the cross-domain request and CORS, which has been challenging to address.

Any insights or assistance would be greatly appreciated. Thank you.

Answer №1

Although it may seem unusual to provide the answer to my own question, I managed to solve the issue a few days after initially posting it.

The root of the problem lies in how browsers handle CORS. When a cross-domain request is made in JavaScript that is not considered "simple" (such as a GET request - which is why the query() function worked), the browser will automatically send an HTTP OPTIONS request to the specified URL/URI, referred to as a "pre-flight" request or "promise". If the remote source responds with a HTTP status code of 200 and includes relevant details about what it accepts in the response headers, then the original JavaScript call will proceed.

Here's a concise example using jQuery:

function makeRequest() {
    // The browser initiates an HTTP OPTIONS request to www.myotherwebsite.com/api/test
    // If it receives a HTTP status code of 200 along with relevant details in the HTTP headers,
    // then it will execute this POST request...
    $.post( "www.myotherwebsite.com/api/test", function(data) {
        alert(data);
    });
    // ...otherwise, it won't proceed - it's that straightforward.
}

To resolve the issue, all I had to do was specify the accepted response headers on the server side:

// Apply these rules to all requests accessing any URL/URI
app.all('*', function(req, res, next) {
    // Add details of allowed 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');
    // Call the next() function to continue execution and proceed to the requested URL/URI
    next();
});

Additionally, include these lines before Express routing to respond with a HTTP 200 status code for every OPTIONS request:

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

I hope this solution assists anyone encountering the same challenge when referring back to this page.

Answer №2

While I haven't personally tested this out, it seems like simply informing the Resource on how to handle the $save request could suffice.

$resource('http://myserver\\:1337/novels/:novelId', { novelId: '@novelId' }, {save: {method: 'PUT'});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Execute identical operations with varying parameters using PM2

I am trying to launch npm start /app1 and npm start /app2 as daemons on an external virtual machine accessed through SSH. Initially, I attempted using PM2 in this manner: $ pm2 start npm -- start --prefix /app1 This method worked for a single applicatio ...

Is it necessary for the paths in an HTML file to be full paths when utilizing Express.static in Node.js?

I have the following files: /public/index.html <!DOCTYPE html> <html> <head> <title>Planner</title> <script src="../codebase/library.js" charset="utf-8"></script> <link hre ...

What is the process for creating separate files for modal controllers?

I have an Angular application with multiple modals, and I am using AngularUI to handle the modal directive. http://angular-ui.github.io/bootstrap/ Currently, my main controllers are all located in the app.js file as routes. $routeProvider.when '/da ...

Implementing OAuth2 in a Microservices architecture to establish a user account

In my current setup, I am utilizing a React Application along with a separate Express API. My goal is to allow users to register through my React app. To do this, I believe the oauth2 flows should follow these steps: Prompt the user for information (suc ...

Storing data in a table created through a belongsToMany relationship in Sequelize and retrieving it. (Solution provided below)

My backend setup includes Node.js, Express.js, and Sequelize for database connections. I have established a many-to-many relationship between Tasks and Keys. I defined the connection between Tasks and Keys using Sequelize as follows: Backend // Task ...

gulp encounters an issue: TypeError('The path provided must be a string. Received ' + inspect(path))

Currently, I am running node v6.2.0 and have a basic gulpfile set up to compile ts files and move some dependencies. However, every time I attempt to transfer the dependencies, I consistently encounter the error mentioned above. gulpfile.js: const gulp ...

Is there a way for me to monitor the progress of a node.js request?

I have a node.js app that creates an image, let's call it "Image A", and then uses this "Image A" to create another image, which we'll refer to as "Composition A". When the server receives 4 image requests almost simultaneously for Composition A, ...

Node.js worker_threads: How to determine when all workers have finished their tasks

Recently dove into learning about worker_threads in nodejs When all the work is done, the final worker should complete the script process.exit(); my index.js const { Worker } = require("worker_threads"); const logUpdate = require("log-update"); ...

Angular Github Deployment Issue: Page malfunctioning as a result of strict-origin-when-cross-origin restriction

I am currently learning Angular and attempting to deploy it on Github Pages. However, I have encountered an issue where the app is not functioning properly. After inspecting the page, I discovered a CORS origin error when trying to access certain resource ...

What is the best way to effectively integrate jQuery plugins into Node.JS?

Getting Started with Node.JS I recently ventured into the world of Node.JS, leveraging my existing expertise in JavaScript and jQuery. While I successfully installed jQuery using npm install jquery, incorporating plugins into my code has proven to be a bi ...

(node) alert: potential EventEmitter memory overflow identified. 11 error listeners have been included. Utilize emitter.setMaxListeners() to enhance the threshold

Recently, I have been working with the pg node module to connect to a database. While performing DML operations, I noticed that multiple connections were being created for each query execution. This led to a warning message stating "(node) warning: possibl ...

What causes SomeFunction.prototype to appear as "SomeFunction {}" when viewed in the console?

This is the code snippet: function Person(){} console.log(Person.prototype); // Person {} console.log(Person.prototype instanceof Person); // false console.log(Person.prototype instanceof Object); // true The output shows Person {} for Person.prototype, e ...

Guideline on extracting private keys from Windows Certificate Manager

I am currently working in a Windows environment setting. Within my organization, we act as our own certificate authority for internally-used https applications. I have obtained a certificate from our system for a private web server I created. While using ...

Uncovering the Power of Mongoose: Maximizing MongoDB with Advanced Search Queries

I need to create a search query that can have a maximum of 5 parameters, but could include any number of the following 5 parameters: 01.name_or_number 02.from_date 03.to_date 04.is_validated 05.is_premium Currently, I am able to c ...

Reducing file size through compression (gzip) on express js version 4.4.1

My express js app is functioning as a web server, but I am having trouble with serving unzipped static content (js and css files). I have tried using compression from https://github.com/expressjs/compression, but it doesn't seem to be working for me. ...

The busboy file size limit event is not triggering

I am currently in the process of uploading a file via Express to a MongoDb database. Everything is functioning properly, but now I am attempting to implement a size limit for the files. The busboy configuration is set as described on the busboy site: var ...

Harness the power of ng-click in conjunction with data-ng-href for a

I am attempting to create a button that takes the user to the product details while also having the ability to increase a counter using an ng-click function. <div class="row center-block save-button" > <a data-ng-href="/savings/{{saving._id}} ...

Comparing app.get() and api.get()/Router.get() methods in ExpressJS

Although I have a basic understanding of this concept, I still believe there is more to learn. Currently, my comprehension includes the fact that app.get() and app.post() are mainly used for making AJAX calls to the server, while Routes are intended for cr ...

Unable to establish a connection between the CloudSQL database and the App Engine utilizing Node JS

I'm having trouble getting my Node.js app on App Engine to connect to Cloud SQL after deploying with gcloud app deploy Upon deployment, I receive the error message Error: connect ENOENT /cloudsql/<INSTANCE_CONNECTION_NAME> at PipeConnectWrap.af ...

Importing an Express app into a Mocha test file automatically initializes the server

Summary: When writing tests in Mocha for an Express-based RESTful HTTP API using Supertest, it has been observed that requiring an instance of the express app can lead to an open connection issue even after the tests have completed and cleaned up. This une ...