简体   繁体   中英

React.js, Express.js and the dreaded CORS

I'm sorry to be posting yet another question about CORS but I just can't figure this one out.

在此处输入图像描述

I have a React app using an Express.js server (running on http://localhost:9001 ) to upload an image to a Google Cloud storage bucket. I keep getting a CORS error even though the image is uploaded successfully and this is preventing me from getting the image's URL returned. I don't really understand how I can get a CORS error even though the image is uploaded but that's what's happening.

I have configured CORS on the Google Cloud storage bucket as follows:

[
    {
      "origin": ["http://localhost:3000"],
      "responseHeader": "*",
      "method": ["POST"],
      "maxAgeSeconds": 3600
    }
]

When I inspect the CORS error I'm getting I see the following:

在此处输入图像描述

The origin is http://localhost:3000 , so that's configured correctly and I'm using POST to upload the image so that should be allowed as well.

The function I've written to upload the image is as follows:

function postImage(file) {
    const formData = new FormData();
    formData.append('file', file);

    fetch(`${window.location.protocol}//${window.location.hostname}:9001/uploads`, {
        method: 'POST',
        mode: 'cors',
        cache: 'no-cache',
        // headers: {
        //     'Content-Type': 'multipart/form-data'
        // },
        body: formData
    })
        // .then((response) => response.json())
        .then((response) => console.log('This is your data:', response.data))

        .catch(error => {
            console.error('There has been a problem uploading your image', error);
        });
}

I've commented out the headers as including them kept throwing up a Multipart: Boundary not found error that I've seen others have an issue with and removing the headers setting hasn't caused any other issues.

I have a helper function on the Express server that uploads the image to the Google Cloud storage bucket:

const uploadImage = (file) => new Promise((resolve, reject) => {
    const { originalname, buffer } = file

    const blob = bucket.file(originalname.replace(/ /g, "_"))
    const filetype = blob.name.split('.').pop()
    const filename = `${uuidv4()}.${filetype}`
    const blobStream = blob.createWriteStream({
        resumable: false
    })
    blobStream.on('finish', () => {
        const publicUrl = format(
            `https://storage.googleapis.com/${bucket.name}/${filename}`
        )
        resolve(publicUrl)
    })
        .on('error', () => {
            reject(`Unable to upload image, something went wrong`)
        })
        .end(buffer)
})

Here are the functions on my Express server:

import { typeDefs } from './graphql-schema'
import { ApolloServer } from 'apollo-server-express'
import express from 'express'
import neo4j from 'neo4j-driver'
import { makeAugmentedSchema } from 'neo4j-graphql-js'
import dotenv from 'dotenv'
import { initializeDatabase } from './initialize'
const bodyParser = require('body-parser')
const multer = require('multer')
const uploadImage = require('./helpers/helpers')

dotenv.config()

const app = express()

    const schema = makeAugmentedSchema({
      typeDefs,
      config: {
        query: {
          exclude: ['RatingCount'],
        },
        mutation: {
          exclude: ['RatingCount'],
        },
      },
    })
    
    const driver = neo4j.driver(
      process.env.NEO4J_URI,
      neo4j.auth.basic(
        process.env.NEO4J_USER,
        process.env.NEO4J_PASSWORD
      ),
      {
        encrypted: process.env.NEO4J_ENCRYPTED ? 'ENCRYPTION_ON' : 'ENCRYPTION_OFF',
      }
    )
    
    const init = async (driver) => {
      await initializeDatabase(driver)
    }
    
    init(driver)
    
    const server = new ApolloServer({
      context: { driver, neo4jDatabase: process.env.NEO4J_DATABASE },
      schema: schema,
      introspection: true,
      playground: true,
    })
    
    // Specify host, port and path for GraphQL endpoint
    const port = process.env.GRAPHQL_SERVER_PORT || 4001
    const path = process.env.GRAPHQL_SERVER_PATH || '/graphql'
    const host = process.env.GRAPHQL_SERVER_HOST || '0.0.0.0'
    
    
    // Code for uploading files to Google Cloud
    app.use((req, res, next, err) => {
      console.error(err.stack)
      res.header("Access-Control-Allow-Origin", "*");
      res.type('multipart/form-data')
      res.status(500).json({
        error: err,
        message: 'Internal server error!',
      })
      next()
    })
    
    const multerMid = multer({
      storage: multer.memoryStorage(),
      limits: {
        // no larger than 5mb.
        fileSize: 5 * 1024 * 1024,
      },
    })
    
    app.disable('x-powered-by')
    app.use(multerMid.single('file'))
    app.use(bodyParser.json())
    app.use(bodyParser.urlencoded({ extended: false }))
    
    app.post('/uploads', async (req, res, next) => {
      try {
        const myFile = req.file
        const imageUrl = await uploadImage(myFile)
        res
          .status(200)
          .json({
            message: "Upload was successful",
            data: imageUrl
          })
      } catch (error) {
        next(error)
      }
    })

    server.applyMiddleware({ app, path })
    
    app.listen({ host, port, path }, () => {
      console.log(`GraphQL server ready at http://${host}:${port}${path}`)
    })
    
    app.listen(9001, () => {
      console.log('Node.js -> GCP server now listening for requests!')
    })  

I've tried a lot of different things to get this working:

  1. I've tried adding http://localhost:9001 to the CORS configuration, as well as other URLs
  2. I've tried opening up all origins with "*" for
  3. I've read through all the documentation [here][3]
  4. I've tried following all the troubleshooting documentation Google has here
  5. I've cleared my browser cache as I've seen that can cause the CORS errors to persist - see another post here
  6. I've tried waiting over night for my Google Cloud CORS configuration to take effect as I've heard the configuration can take a bit of time to propagate

Despite all of this I'm still getting the CORS error but my upload is still working. I just need to clear the error so I can get the returned image URL.

You add cors to Google Cloud storage bucket but you forgot to add it to express server POST function. Or use it as global on your express server.

Try this on your express POST function:

res.header("Access-Control-Allow-Origin", "http://example.com");

Or

res.header("Access-Control-Allow-Origin", "*");

Or even better:

    /* Headers */
app.use((req, res, next) => {
    res.header("Access-Control-Allow-Origin", "*"); // update to match the domain you will make the request from
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    next();
});

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