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

CORS problem with http OPTIONS #1325

Closed
m00zi opened this issue Jun 8, 2018 · 34 comments
Closed

CORS problem with http OPTIONS #1325

m00zi opened this issue Jun 8, 2018 · 34 comments

Comments

@m00zi
Copy link

m00zi commented Jun 8, 2018

I have the following environment setup on my PC:

  • Nodejs version (v10.1.0)
  • Angular version Angular CLI: 6.0.1
    Node: 10.1.0
    OS: darwin x64

I have problem with nodejs in https mode only, once the https mode is enabled on nodejs, I am not able to get any response from the server for POST request, but the GET requests are fine,
but once I switched back to normal http, the POST also working fine,

I have enabled cors in my nodejs in 2 method both are not working with https, I have restarted my PC and even create new nodejs app, same issue,

here is my first method to enable cors:

app.use((req, res, next) => {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
    if (req.method === 'OPTIONS') {
        res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
        res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH');
        return res.status(200).json({});
    };
    next();
});

and here is the secod method for enabling cors, I have used 3rd party library:

var cors = require('cors');
app.use(cors());

The nodejs code:

'use strict';
var express = require('express');
var cors = require('cors');
var bodyParser = require('body-parser');
var app = express();
var router = express.Router();
var fs = require('fs');

var secureOptions = {
    key: fs.readFileSync('./ssl/key.pem'),
    cert: fs.readFileSync('./ssl/certificate.pem')
};

var server = require('https').createServer(secureOptions, app);

server.listen(3000, () => {
    console.log('server is running on port 3000')
});

app.use('/', router);


router.use((req, res, next) => {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
    if (req.method === 'OPTIONS') {
        res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
        res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH');
        return res.status(200).json({});
    };
    next();
});
    

//router.use(cors());
router.use(bodyParser.urlencoded({extended: false}));
router.use(bodyParser.json());



router.post('/login', (req, res, next) => {
    console.log(req.body);
    res.send('BODY ==> ' + JSON.stringify(req.body));
});

this is my authService in angular:

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  apiUrl = 'https://127.0.0.1:3000'
  httpOptions = {
    headers: new HttpHeaders({
      'Content-Type': 'application/json'
    })
  }

  constructor(private http: HttpClient) { }


  login(username: String, password: String): Observable<any[]> {
    return this.http.post<any[]>(this.apiUrl + '/login', {username, password}, this.httpOptions);
  }

}

this is my component:

import { Component } from '@angular/core';
import { AuthService } from './auth.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app';
  constructor(private authService: AuthService) {}

doLogin(f: any) {
  this.authService.login(f.username, f.password)
    .subscribe(
      (data => {
        console.log(`data ==> ${data}`);
      }),
      (error => {
        console.log(`error ==> ${JSON.stringify(error)}`);
      })
    )

}

}

this is the html:

<form #loginForm="ngForm" (ngSubmit)="doLogin(loginForm.value)">
  Usernam: <input type="text" name="username" ngModel>
  <br>
  Password: <input type="password" name="password" ngModel>
  <br>
  <button type="submit">Login</button>
</form>

this is the console output:

Angular is running in the development mode. Call enableProdMode() to enable the production mode.
zone.js:2969 OPTIONS https://127.0.0.1:3000/login 0 ()
scheduleTask @ zone.js:2969
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.scheduleTask @ zone.js:407
onScheduleTask @ zone.js:297
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.scheduleTask @ zone.js:401
push../node_modules/zone.js/dist/zone.js.Zone.scheduleTask @ zone.js:232
push../node_modules/zone.js/dist/zone.js.Zone.scheduleMacroTask @ zone.js:255
scheduleMacroTaskWithCurrentZone @ zone.js:1114
(anonymous) @ zone.js:3001
proto.(anonymous function) @ zone.js:1394
(anonymous) @ http.js:1639
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe @ Observable.js:161
(anonymous) @ subscribeTo.js:21
subscribeToResult @ subscribeToResult.js:6
push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapSubscriber._innerSub @ mergeMap.js:127
push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapSubscriber._tryNext @ mergeMap.js:124
push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapSubscriber._next @ mergeMap.js:107
push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next @ Subscriber.js:93
(anonymous) @ scalar.js:5
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable._trySubscribe @ Observable.js:176
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe @ Observable.js:161
push../node_modules/rxjs/_esm5/internal/operators/mergeMap.js.MergeMapOperator.call @ mergeMap.js:80
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe @ Observable.js:158
push../node_modules/rxjs/_esm5/internal/operators/filter.js.FilterOperator.call @ filter.js:55
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe @ Observable.js:158
push../node_modules/rxjs/_esm5/internal/operators/map.js.MapOperator.call @ map.js:51
push../node_modules/rxjs/_esm5/internal/Observable.js.Observable.subscribe @ Observable.js:158
push../src/app/app.component.ts.AppComponent.doLogin @ app.component.ts:18
(anonymous) @ AppComponent.html:1
handleEvent @ core.js:9953
callWithDebugContext @ core.js:11046
debugHandleEvent @ core.js:10749
dispatchEvent @ core.js:7415
(anonymous) @ core.js:8892
schedulerFn @ core.js:3415
push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.__tryOrUnsub @ Subscriber.js:253
push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.next @ Subscriber.js:191
push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber._next @ Subscriber.js:129
push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next @ Subscriber.js:93
push../node_modules/rxjs/_esm5/internal/Subject.js.Subject.next @ Subject.js:53
push../node_modules/@angular/core/fesm5/core.js.EventEmitter.emit @ core.js:3395
push../node_modules/@angular/forms/fesm5/forms.js.NgForm.onSubmit @ forms.js:3384
(anonymous) @ AppComponent.html:1
handleEvent @ core.js:9953
callWithDebugContext @ core.js:11046
debugHandleEvent @ core.js:10749
dispatchEvent @ core.js:7415
(anonymous) @ core.js:7859
(anonymous) @ platform-browser.js:1140
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:421
onInvokeTask @ core.js:3662
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:420
push../node_modules/zone.js/dist/zone.js.Zone.runTask @ zone.js:188
push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask @ zone.js:496
invokeTask @ zone.js:1540
globalZoneAwareCallback @ zone.js:1566
app.component.ts:23 error ==> {"headers":{"normalizedNames":{},"lazyUpdate":null,"headers":{}},"status":0,"statusText":"Unknown Error","url":null,"ok":false,"name":"HttpErrorResponse","message":"Http failure response for (unknown url): 0 Unknown Error","error":{"isTrusted":true}}

https://pasteboard.co/HoY6SfB.png

@Klimm-Max
Copy link

Having the same issue. Did you have any workaround yet ?
thx already.

@Klimm-Max
Copy link

Klimm-Max commented Jul 25, 2018

i am using
app.use(cors());
on the server.js file. (it's working now.)

@jugol
Copy link

jugol commented Jan 9, 2019

neither working to me .. T_T

@hamza-khan-mhk1
Copy link

hamza-khan-mhk1 commented Jan 23, 2019

you need to allow both:

  1. Http.OPTIONS method for request that is hitting " apiUrl =https://127.0.0.1:3000/login".
  2. Allow CORS like below:

res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'content-type');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');

@hamza-khan-mhk1
Copy link

hamza-khan-mhk1 commented Jan 23, 2019

neither working to me .. T_T

check my answer, hope that helps you as well

@m00zi
Copy link
Author

m00zi commented Jan 23, 2019

check out this link:

dialogflow/dialogflow-javascript-client#86 (comment)

@hamza-khan-mhk1
Copy link

check out this link:

dialogflow/dialogflow-javascript-client#86 (comment)

does it work for prod env.?

@gireeshpunathil
Copy link
Member

@m00zi - is this still an issue?

@jolu71679
Copy link

yes. I am using v10 Nodejs when developing an application in office.js
I am using HTPPS tp do get/post queries, but I am experiencing a problem with reading the data sent back from the server.
While I see the server got the requests and send back a response, it seems that the events on the client side are not fired at all!
I cant understand why?
I would appreciate very much your help resolving that as I am blocked on it for quite a long time

@jolu71679
Copy link

jolu71679 commented Jun 12, 2019

image

This is the code in the client side ( the office.js)

@gireeshpunathil
Copy link
Member

can you print the error object in the error listener on the request? to see what we get there? also worthwhile to put a debug statement (a console log message) in the start of the request callback - just to make sure we hit the callback in the first place.

@jolu71679
Copy link

@gireeshpunathil Thankn you for your answer!
The error callback function is not triggered at all, as all the other events.
range2.values= [[100]] is the manner to print. It prints the value in excel sheet.
So, since none of prints happen I understood none of the events had been trigered and I cant understand why

@gireeshpunathil
Copy link
Member

range2.values= [[100]] is the manner to print.

this part I am lacking clarity. how? Is that how node interacts with angular? I am sorry; I don't have any idea on that part.

on the other hand, what if you use console.log in those callbacks? where does the prints go?

@gireeshpunathil
Copy link
Member

thinking further: what if you just run this client (the code in your black screen above) as a standalone node program ? and then comment out all the range... assignments, instead use prints? that way we can figure out whether there is an issue with the network, connection, socket, events and callback?

@jolu71679
Copy link

if I use console.log it doesnt print anywhere ..
here is a screen shot of npm consule as I start the program (npm start..)
so there is no where to print when i write console.log

@jolu71679
Copy link

jolu71679 commented Jun 12, 2019

image

if console.log was to work where it should have been printed ?

@gireeshpunathil
Copy link
Member

ok, let us try console.error API? may be some code would have overridden console.log and suppress the prints.

@jolu71679
Copy link

console.error also doenst print anything..
I think there is no error since the server is returning the value as expected, there is an issue with the events, they arent fired for some reason, meaning that those functions are not executed at all. (I have printed it just before sending it to the client, and saw the response..here is a screenshot of the print of the server side right before sending back to the client:
image

@gireeshpunathil
Copy link
Member

ok, somewhere in between my suggestion got lost. Let me put it again: can you please run this and share the output?

const options = {
  hostname: 'localhost',
  port: 8888,
  path: '/getLastIdOrder',
  method: 'Get'
}

const req = https.request(options, (res) => {
  console.log('response')
  res.on('data', (d) => {
    console.log(`data: ${d.toString()}`)
  })
  res.on('end', () => {
    console.log('response ended')
  })
})

req.on('error', (e) => {
  console.log(`request error: ${e}`)
})
req.end()

@jolu71679
Copy link

Hi @gireeshpunathil
I may have a lead..Since I am running the the HTTPS request inside an async function, The function doest wait for the response and just continue executing. And when we finally get the response from the server we lost the context which we so much need in Office programing and the code doesnt know what to do with the response..
So sometimes the client side does get the answer from the server and can manipulate it, but sometimes it doesnt... Thus, cant rely on it.

so the question is what should I do to make the code wait for the answer of the request and execute the rest of the function? (I already tried to use await on https.request() ,but it didnt solve the problem.

At least, now we know that the https response events are fired! but practically I cant be sure to have the data returned to work on..

@jolu71679
Copy link

image

@gireeshpunathil
Copy link
Member

that is great! thanks for clarifying!

And when we finally get the response from the server we lost the context which we so much need in Office programing and the code doesnt know what to do with the response..

so the code which you are talking about here: i.e., the one that keeps running or the one that lost the context with the server response - can you please put it in the response's end callback? also make sure you cache the data on the data callback, within the request callback's scope. This cached data will be available to the code that is written inside the end callback. And that provides the context.

@jolu71679
Copy link

If I put the return of the sync function inside the response callback, nothing happen any more!

what do you mean by : "also make sure you cache the data on the data callback, within the request callback's scope."
inside the res.on('data') I got the data returned by the server and assigned it to the body variable which available every where in the code..

@gireeshpunathil
Copy link
Member

nothing happen any more!
does it mean things are solved, or things are stuck at the same place?

on your second part, I agree: the body variable is available everywhere. I was explaining to your concern on we lost the context part, which I guess is not a problem anymore, so let us park it!

@jolu71679
Copy link

no, I still ahve the same problem.
Sometimes the above code (client side which is the office code) does manage to get the data from the server and do calculation on it and then display it to the client on excel. But sometimes the code doesnt get the data at time and it seems the async function finishes its work before getting and processing the data return from the server..

@gireeshpunathil
Copy link
Member

AFAIK, the synchronization context that is used in your code is not native to node.js so I cannot say for sure what / where the problem is. If you fully remove the async-await-sync construct and resort to only callback, then placing the client code in the response' end callback is the right thing to do.
Let us see if some one else provide a more tangible suggestion!

@gireeshpunathil
Copy link
Member

@jolu71679 - were you able to try my suggestion?

@PedroRib76
Copy link

@hamza-khan-mhk1
Thank you very much, your recommendation worked just fine for my problem with CORS. Thank you.

@hari0206
Copy link

hari0206 commented Nov 8, 2019

@m00zi - is this still an issue?

yes

@m00zi
Copy link
Author

m00zi commented Nov 16, 2019

you could use proxy to solve this issue:
under the root directory of your angular project (in the same level of package.json create new file name it proxy.json add the below content so all your API calls toward backend will be done through a proxy and no CORS issue anymore, by the way using this way for production also is secure than call backend directly :
let's assume you are serving API under /api on your backend server so :

STEP1:

{
    "/api": {
        "target": "https://127.0.0.1:3000",
        "secure": "false",
        "changeOrigin": true
    }
}

and in your angular service file you dont need to add full url in order to get or post:

STEP 2:

getEmps() {
    return this.http.get('/api/emps')
  }

STEP 3:

edit your package.json file :

"start": "ng serve --proxy-config proxy.json --ssl true",

Done.

@smaillns
Copy link

smaillns commented Mar 9, 2020

Better solution is to add some configuration in your server

app.use(function(req, res, next) {
    res.header("Access-Control-Allow-Origin", url);
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
    next();
  });

So in your header you specify the allowed referer url, for example "http://localhost:8100", which is the frontend url as well

@m00zi
Copy link
Author

m00zi commented Mar 10, 2020

@m00zi - is this still an issue?

yes

did you tried the steps as I explained ? #1325 (comment)

@PoojaDurgad
Copy link

@hari0206 , have you got this issue resolved? this issue seems to be resolved, and can be closed.

@gireeshpunathil
Copy link
Member

inactive, and / or resolved, closing. pls feel free to reopen if required

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

10 participants