Andreas Vitzthum
Andreas Vitzthum

Reputation: 11

Adding cors to MEAN Singlepoint Application, for Swagger-ui

Im running a NodeJS server which is serving the UI with the builded Angular /dist folder.

In the UI I include the swagger-ui, which is loading an swagger.json, the *.json is describing a REST interface and within the swagger-ui you should be able to test REST Interfaces.

https://swagger.io/swagger-ui/

Project structure

enter image description here

In the server.js I added a fixed rout to the /dist path where the index.html is stored, also there are express routes to the rest Interfaces which my server is offering. to load the swagger.json Documentation files

server.js

// Get dependencies
const dotEnv      = require('dotenv').config();
const express     = require('express');
const path        = require('path');
const http        = require('http');
const bodyParser  = require('body-parser');
var mongoose      = require('mongoose');
const cors        = require('cors');
// Get our API routes
const api         = require('./server/routes/api');
const swaggerAPI  = require('./server/routes/swaggerAPI');
const app         = express();


var connectionUrl = typeof process.env.CONNECTION_URL  !== 'undefined' ?  process.env.CONNECTION_URL  : 'mongodb://db:27017/docdb';
console.log("Connection URL: " + connectionUrl);
mongoose.connect(connectionUrl);

// Parsers for POST data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

// Point static path to dist
app.use(express.static(path.join(__dirname, 'dist')));

// Set our api routes
app.use('/api', api);
app.use('/swagger-api', swaggerAPI);

app.use('/client', express.static('client'));

app.use(cors());
app.use(function (req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With");
    res.header("Access-Control-Allow-Methods", "GET, PUT, POST");
    if ('OPTIONS' === req.method) {
        res.status(204).send();
    }
    else {
        next();
    }
});

// Catch all other routes and return the index file
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'dist/index.html'));
});

/**
 * Get port from environment and store in Express.
 */
const port = process.env.PORT || '3000';
app.set('port', port);

/**
 * Create HTTP server.
 */
const server = http.createServer(app);

/**
 * Listen on provided port, on all network interfaces.
 */
app.listen(port, () => console.log(`API running on localhost:${port}`));

Everthing works fine I can load the swagger.json with my REST interfaces, persist them in a MongoDB and show them in the Angular UI. But when I want to test REST interfaces from the swagger-ui i get an error in console:

 Failed to load http://localhost:8888/****/Y7CTQW5PTSEG1MMPN: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

But when I debug the Request in Chrome, I can see the loaded data in the Network tab.

Why is the console showing the cors error, while loading the data and not showing it in the ui?

Upvotes: 0

Views: 1544

Answers (1)

Neha Tawar
Neha Tawar

Reputation: 705

When Site A tries to fetch content from Site B, Site B can send an Access-Control-Allow-Origin response header to tell the browser that the content of this page is accessible to certain origins. (An origin is a domain, plus a scheme and port number.) By default, Site B's pages are not accessible to any other origin; using the Access-Control-Allow-Origin header opens a door for cross-origin access by specific requesting origins.

For each resource/page that Site B wants to make accessible to Site A, Site B should serve its pages with the response header:

Access-Control-Allow-Origin: http://siteA.com Modern browsers will not block cross-domain requests outright. If Site A requests a page from Site B, the browser will actually fetch the requested page on the network level and check if the response headers list Site A as a permitted requester domain. If Site B has not indicated that Site A is allowed to access this page, the browser will trigger the XMLHttpRequest's error event and deny the response data to the requesting JavaScript code.

Supposing that Site A wants to send a PUT request for /somePage, with a non-simple Content-Type value of application/json, the browser would first send a preflight request:

OPTIONS /somePage HTTP/1.1
Origin: http://siteA.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type

Note that Access-Control-Request-Method and Access-Control-Request-Headers are added by the browser automatically; you do not need to add them. This OPTIONS preflight gets the successful response headers:

Access-Control-Allow-Origin: http://siteA.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type

When sending the actual request (after preflight is done), the behavior is identical to how a simple request is handled. In other words, a non-simple request whose preflight is successful is treated the same as a simple request (i.e., the server must still send Access-Control-Allow-Origin again for the actual response).

The browsers sends the actual request:

PUT /somePage HTTP/1.1
Origin: http://siteA.com
Content-Type: application/json

{ "myRequestContent": "JSON is so great" } And the server sends back an Access-Control-Allow-Origin, just as it would for a simple request:

Access-Control-Allow-Origin: http://siteA.com See Understanding XMLHttpRequest over CORS for a little more information about non-simple requests. Please check this links as well to solve and fix your problem.

Cors content tutorial

Using cors

fixing cors

Upvotes: 1

Related Questions