简体   繁体   中英

how to configure server created with createServer to fullfill cors when it is already created?

I have this code:

var gqlServer =require('./server.js')

var server=gqlServer()
var port = process.env.PORT||5000

server.listen({port:port}, ()=>
  console.log(`🚀 Server ready at http://localhost:${port}`)
)

and server.js being:

var cors=require('cors')
const http=require('http')
const express = require('express')
const {ApolloServer,gql,PubSub} = require('apollo-server-express')
var firebase =require('firebase/app')
require('firebase/database')

// const pubsub=new PubSub()

const app = express()
var whiteList=['https://totorotos.firebaseapp.com',
'https://totorotos.web.app','http://localhost:8080']
app.use(cors({origin:whiteList}))
const httpServer=http.createServer(app)
var io = new require('socket.io')(httpServer,{
  origins:whiteList
})

const firebaseConfig = {
    apiKey: "*********",
    authDomain: "******",
    databaseURL: "********",
    projectId: "********i",
    storageBucket: "********",
    messagingSenderId: "********",
    appId: "********",
    measurementId: "********"
  }

firebase.initializeApp(firebaseConfig)
var database = firebase.database()

const typeDefs = gql`
  type Query {
    hello: String,
    getEmails:[String!]
    getPosts(email:String): [GetPosts]
  }
  type Mutation{
    postMessage(message:String,email:String!,
      name:String,photo:String,date:String!): Boolean
  }
  type GetPosts{
    email:String!
    name:String
    date:String!
    photo:String
    message:String
  }
`

const resolvers = {
  Query: {
    hello: () => 'Hello roger!',
    getEmails:async()=>{
      const result=[]
      await database.ref('posts/').orderByChild('email')
      .once('value', function(snapshot) {
        snapshot.forEach(function(childSnapshot) {
          var childKey = childSnapshot.key
          var childData = childSnapshot.val()
          result.push(childData.email)
        })
      })
      const res2= [...new Set(result)]
      return res2
    },
    getPosts:async(_,args)=>{
      const result=[]
      await database.ref('posts/').orderByChild('email').equalTo(args.email)
      .once('value', function(snapshot) {
        snapshot.forEach(function(childSnapshot) {
          var childKey = childSnapshot.key
          var childData = childSnapshot.val()
          result.push(childData)
        })
      })
      return result
    }
  },
  Mutation:{
    postMessage:async(_,args)=>
    {      
      await database.ref('posts/').push({
        message: args.message,
        email:args.email,
        name:args.name,
        photo:args.photo,
        date:args.date
      })
      io.emit('updated', {email:args.email})
    //   pubsub.publish('POST_ADDED',{name:'paquito',
    // comment:'como estas tronco?'});
      return true
    }
  },
  // Subscription: {
  //   postAdded: {
  //     subscribe: () => pubsub.asyncIterator(['POST_ADDED']),
  //     resolve: payload => {
  //       return payload
  //     }
  //   }
  // }
}

function gqlServer() {
  const server = new ApolloServer({
    typeDefs,
    resolvers,
    // Enable graphiql gui
    introspection: true,
    playground: true
  })

//   var whiteList=['https://totorotos.firebaseapp.com',
// 'https://totorotos.web.app']
  // server.applyMiddleware({app, path: '/', cors: {origin:whiteList,credentials:true}})
server.applyMiddleware({app,path:'/',cors:false})

  // server.installSubscriptionHandlers(httpServer)
  return httpServer
}

module.exports = gqlServer

and this works fine. that is when localhost:8080 tries to connect to localhost:5000 this works. the problem comes when I publish the firebase cloud function with this:

const functions = require('firebase-functions')
const express=require('express')
const cors=require('cors')({ origin: true })
const admin = require('firebase-admin')
const gqlServer=require('./server/index')

admin.initializeApp();
var server=gqlServer()
exports.api1=functions.https.onRequest(server)

being server/index the same as before ( server.js ). with that, I get an error (cors issue) when trying to connect to the url cloud function from totorotos.web.app . how can I configure server to accomplish cors when it is already created? Any help in any sense will be very appreciated. Thank you.

For enabling CORS with a plain nodejs http server (no Express framework being used), there is no middleware architecture that can set the desired headers and then continue routing to the other request handlers so you have to do it IN the actual request handler.

FYI, the middleware architecture is one of the primary benefits of the Express framework which allows you to set CORs policies in one place that lots of request handlers can use.

Anyway, here's what it would look like for a plain nodejs http server incoming request.

// add new listener to the http server for requests
server.on('request', (req, res) => {
    // check if this is the path we are interested in
    // if there could be query parameters, then you have to parse them off first
    if (req.url === "/somePath") {
        // If desired, set these more specifically such as only specific origins
        // or CORS only allowed on specific methods
        const headers = {
            'Access-Control-Allow-Origin': '*',     
            'Access-Control-Allow-Methods': 'OPTIONS, POST, GET',
            // add other headers as needed
        };

        // if pre-flight request, handle it here
        if (req.method === 'OPTIONS') {
            res.writeHead(204, headers);
            res.end();
            return;
        } else if (req.method === "GET") {
            res.writeHead(200, headers);
            // handle rest of the GET request here
            // ...
            res.end();
        } else {
            res.writeHead(405);
            res.end();
        }
    }
});

Here's an article on the general topic: Enable CORS in nodejs http server without Express .

Handling the OPTIONS request is something that many people don't understand or don't know to put in. For CORS, this is called a "pre-flight" request and under some circumstances, the browser will use the OPTIONS request as a permissions check to see if the server allows the request before actually sending the real request. If the browser chooses to do that, the pre-flight request must also have appropriate CORS headers as that's how the browser decides if the request is allowed or not.

There are a variety of circumstances that will cause the browser to issue the pre-flight request. In a nutshell, it's any request that the CORS specification doesn't define as "simple". You can read more about that here . Things that can make a request not be simple are: custom headers beyond a few select ones, any verb other than GET, HEAD or POST, Content-Type header on the request other than application/x-www-form-urlencoded , multipart/form-data or text/plain and so on...

If you require credentials be sent with the cross origin request (such as cookies), then there are further restrictions you can read about here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM