Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do we handle cors? #19

Open
ctippur opened this issue Mar 30, 2020 · 15 comments
Open

How do we handle cors? #19

ctippur opened this issue Mar 30, 2020 · 15 comments

Comments

@ctippur
Copy link

ctippur commented Mar 30, 2020

Hello,
I am struggling to add CORS support to my app running on swagger server. I tried using swagger-express-middleware but that dint do the trick. Looks like they are not yet supporting openapi 3.0 specs.

Need some pointers please.

I have a middleware code as shown in the example:

'use strict';

var path = require('path');
var http = require('http');

var oas3Tools = require('oas3-tools');
var serverPort = 8080;
const createMiddleware = require('@apidevtools/swagger-express-middleware');

// swaggerRouter configuration
var options = {
    controllers: path.join(__dirname, './controllers')
};

var expressAppConfig = oas3Tools.expressAppConfig(path.join(__dirname, 'api/openapi.yaml'), options);
expressAppConfig.addValidator();
var app = expressAppConfig.getApp();

createMiddleware( __dirname + '/api/openapi.yaml', app, function(err, middleware) {
    app.use(
        middleware.metadata(),
        middleware.CORS(),
        middleware.files(),
        middleware.parseRequest(),
        middleware.validateRequest(),
        middleware.mock()
    );

    // Show the CORS headers as HTML
    app.use(function(req, res, next) {
        res.send('<pre>' + util.inspect(res._headers) + '</pre>');
    });

    app.listen(8080, function() {
        console.log('Go to http://localhost:8080/docs');
    });
});

My curl call is resulting in a 405 error.

 curl -I -v http://localhost:8080/v2/url/my_test_alias
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> HEAD /v2/url/ali_test_alias HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 405 Method Not Allowed
HTTP/1.1 405 Method Not Allowed
< X-Powered-By: Express
X-Powered-By: Express
< Content-Type: application/json; charset=utf-8
Content-Type: application/json; charset=utf-8
< Content-Length: 118
Content-Length: 118
< ETag: W/"76-HftMGD7rVmxkEyOoP10+GyQtLQY"
ETag: W/"76-HftMGD7rVmxkEyOoP10+GyQtLQY"
< Date: Sat, 28 Mar 2020 00:52:39 GMT
Date: Sat, 28 Mar 2020 00:52:39 GMT
< Connection: keep-alive
Connection: keep-alive

< 

I am sure I am doing something wrong. Appreciate any pointers.

@ctippur
Copy link
Author

ctippur commented Jul 4, 2020

Any takers on this pls?

@inap-bannai
Copy link

inap-bannai commented Aug 27, 2020

var cors = require("cors");
...
var app = expressAppConfig.getApp();
app.use(cors());

Hope this would be helpful.

@thomasgainant
Copy link

thomasgainant commented Feb 3, 2021

Hello,

The problem comes from the fact that this plugin configure the app in a bad way. Just to deactivate a type of logs, I had to create a pull request with a simple parameter which deactivate logging.

Your problem comes from the configuration of the app in the ExpressAppConfig of the module:

class ExpressAppConfig { constructor(definitionPath, appOptions) { this.definitionPath = definitionPath; this.routingOptions = appOptions.routing; this.setOpenApiValidatorOptions(definitionPath, appOptions); this.app = express(); const spec = fs.readFileSync(definitionPath, 'utf8'); const swaggerDoc = jsyaml.safeLoad(spec); this.app.use(bodyParser.urlencoded()); this.app.use(bodyParser.text()); this.app.use(bodyParser.json()); this.app.use(this.configureLogger(appOptions.logging)); this.app.use(express.json()); this.app.use(express.urlencoded({ extended: false })); this.app.use(cookieParser()); const swaggerUi = new swagger_ui_1.SwaggerUI(swaggerDoc, appOptions.swaggerUI); this.app.use(swaggerUi.serveStaticContent()); this.app.use(OpenApiValidator.middleware(this.openApiValidatorOptions)); this.app.use(new swagger_parameters_1.SwaggerParameters().checkParameters()); this.app.use(new swagger_router_1.SwaggerRouter().initialize(this.routingOptions)); this.app.use(this.errorHandler); }

Here you can see that the whole stack of middleware is injected ("app.use") in the app before defining the routes from the open api contract. The problem is that it is really hard to modify the Express middleware stack once you have set the routes.

Using app.use(cors()); after you received your app from expressAppConfig, like suggested above without testing it, will be simply ignored because the middleware layers added before by the app configurator will fill some unknown logic before getting to the one added by the cors middleware (probably some header "Access-Control-Allow-Origin" related logic).

So, if you modify the module and simply add:

this.app.use(express.urlencoded({ extended: false })); this.app.use(cookieParser()); **this.app.use(cors());** const swaggerUi = new swagger_ui_1.SwaggerUI(swaggerDoc, appOptions.swaggerUI); this.app.use(swaggerUi.serveStaticContent());

You can now use CORS without problems.

In the future, this should be included by default in the module as an option.

@HugoMario
Copy link
Collaborator

thanks a lot @thomasgainant for your comment.
I'll keep this in mind for future changes.

@IselaMorquecho
Copy link

I think that the same happens with errorHandler, I haven't override default error format

@bognari
Copy link

bognari commented Apr 11, 2021

my solution for this problem was this:

const openApiApp = expressAppConfig.getApp();

const app = express();

// Add headers
app.use(/.*/, cors());

for (let i = 2; i < openApiApp._router.stack.length; i++) {
    app._router.stack.push(openApiApp._router.stack[i])
}

@slavomir-sidor
Copy link

Hi,

i just fixed it in my fork https://github.com/slavomir-sidor/oas3-tools

you can use it in your index.js

'use strict';

var path = require('path');
var http = require('http');

var oas3Tools = require('oas3-tools');
var serverPort = 8080;
app=express()
// swaggerRouter configuration
var options = {
routing: {
controllers: path.join(__dirname, './controllers')
},
cors:{
"optionsSuccessStatus": 200,
"credentials": true,
"origin": ["http://localhost:4200"],
"methods": "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS,CONNECT,TRACE",
"exposedHeaders": true,
"preflightContinue": false,
}
};

var expressAppConfig = oas3Tools.expressAppConfig(path.join(__dirname, 'api/openapi.yaml'), options);
var app = expressAppConfig.getApp();

// Initialize the Swagger middleware
http.createServer(app).listen(serverPort, function () {
console.log('Your server is listening on port %d (http://localhost:%d)', serverPort, serverPort);
console.log('Swagger-ui is available on http://localhost:%d/docs', serverPort);
});

@brnprasad
Copy link

Hi Slavomir, Thanks for the updates.
In our project we want to use two Swagger definitions, one for the customer and one for internal testing purpose. I want to know how to integrate that in the same project.
When we create server side node js code (which consists of the apis to be given to the customer), we get the directory structure as follows:
app
|__ openapi.yaml
controllers
|__ Default.js
service
|__ DefaultService.js
utils
|__ writer.js
index.js
In this directory structure, we edit the index.js and add the code similar what you have explained above.

Now If i create another server side nodejs code (for internal) the same directory structure repeats.
Now My question is how to add the code in the secondly generated nodejs code index.js.
We may need to use different server port may be 8081.
Can you please give your suggestions how to handle this.

@matteoxplo
Copy link

matteoxplo commented May 25, 2022

my solution for this problem was this:

const openApiApp = expressAppConfig.getApp();

const app = express();

// Add headers
app.use(/.*/, cors());

for (let i = 2; i < openApiApp._router.stack.length; i++) {
    app._router.stack.push(openApiApp._router.stack[i])
}

I've found that when you add a middleware to express with app.use it simply add it to the end of the stack (here you can see the app.use function).
The problem with oas3-tools is in the line 50 of express.app.config.js, and happens because middlewares are executed in the same order of the stack array therefore every middleware after that will be "skipped".

I've wrote a workaroudn that firstly add the middleware to the stack, then move the "SwaggerRouter" and the "ErrorHandler" at the end of the stack (after the new middleware)

function insertMiddleware(app, middleware) {
    const stackLength = app._router.stack.length;
    app.use(middleware);
    app._router.stack.push(...app._router.stack.splice(stackLength - 2, 2));
}

insertMiddleware(app, cors());

I'm not an expert therefore i'm not sure that works every time, but i've tried with cors and it works.

P.S. i've noticed that with the newer version the problem should be fixed, but it is not released in npm

@fgarciadevelop
Copy link

I still facing with this problem.

I can solve it on local with the solutions proposed, but I can't fix it on my remote server. I deploy the API server with docker-compose and I can't make this patch on the docker image.

Has someone a solution for this?

@inoyakaigor
Copy link

For one's who looking for easy solution: go to generated code utils/writer.js and modify resposne.writeHead code:

response.writeHead(code, {
    'Content-Type': 'application/json',
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS,CONNECT,TRACE"
  });

@Naybs808
Copy link

Naybs808 commented May 2, 2024

Have tried all the suggested solutions in this thread in my code and still running into the same issue. Anyone have an updated suggestion?

Edit:

Was a bit put off editing the source code as it seems a bit extreme, most other server implementations I've used have an option you can set that will work for this kind of thing.

Making the change to the source of the oas3-tools source as suggested by @thomasgainant resolved the issue for me. Thank you.

@bouchaala-sabri
Copy link

bouchaala-sabri commented Jun 5, 2024

This is how I made it

const app = expressAppConfig.getApp();

app.use(cors());

// Get a reference to the router stack
let stack = app._router.stack;

// Find the indices of corsMiddleware and expressInit
let corsIndex = stack.findIndex(layer => layer.name === 'corsMiddleware');
let expressInitIndex = stack.findIndex(layer => layer.name === 'expressInit');

// Check if both middleware are in the stack
if (corsIndex !== -1 && expressInitIndex !== -1) {
    // Remove corsMiddleware from its current position
    let corsMiddleware = stack.splice(corsIndex, 1)[0];

    // If cors was after expressInit in the stack, decrement expressInitIndex
    if (corsIndex > expressInitIndex) {
        expressInitIndex--;
    }

    // Insert corsMiddleware before expressInit
    stack.splice(expressInitIndex, 0, corsMiddleware);
}

@igoriando
Copy link

Thanks @bouchaala-sabri , this is elegant workaround and it works

@igoriando
Copy link

For one's who looking for easy solution: go to generated code utils/writer.js and modify resposne.writeHead code:

response.writeHead(code, {
    'Content-Type': 'application/json',
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS,CONNECT,TRACE"
  });

This works with simple requests, but for all others it will fail to answer preflight check. For non-simple requests, browser sends OPTIONS and expects 2xx response, Express responds with 405 Method not allowed, unless you properly initialize CORS middleware...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests