由于CORS选项预检,ReactJS无法发送POST请求

时间:2018-06-22 15:38:57

标签: javascript node.js reactjs express graphql

每当我向服务器发送POST请求时,{strong>总是会返回OPTIONS

我有一个使用ReactApollo的非常简单的登录应用程序。当我提交表单时,应该使用 POST 请求将mutation发送到服务器,但是 OPTIONS 总是会拦截。

您可以在此处看到(使用chrome ): enter image description here

但是当我使用 Firefox 时,在OPTIONS之后,接下来需要 POST ,如下所示:

enter image description here

我知道我无法禁用预检选项,因为根据我的研究,使用Content-Type: application/json / bodyParser.json()会触发预检。

但是我需要Content-Type: application/json才能使graphql工作。但是有什么可能的方法只是 POST 请求而忽略 OPTIONS ?  我需要POST请求返回的this.props.data enter image description here

所以我可以将令牌存储在客户端浏览器的localStorage中...

我的问题是:

  • 由于OPTIONS(chrome),POST请求从未发生
  • 我无法访问React组件中的this.props.data,因为 OPTIONS总是在实际的 POST (在Firefox中)
  • 之前返回 FIRST

我只想从POST请求中获取data

这是cors的问题吗?有人可以启发我做错了什么吗?非常感谢您。

PS: 我尝试了以下链接:https://github.com/graphql/express-graphql/issues/14,但仍然无法解决此问题...

这是我的服务器:

const express = require('express')
const morgan = require('morgan')
const bodyParser = require('body-parser')
// Grahql
const expressGraphQL = require('express-graphql')
const jwt = require('jsonwebtoken')
const cors = require('cors')
const chalk = require('chalk')

const model = require('./db/models')
const schema = require('./schema/schema')

const app = express()
app.use(cors())
app.options('*', cors())
app.use(bodyParser.json())
app.use(morgan('dev'))

// secret key
const SECRET = 'eggieandsausage'

// this method checks token authenticity from
// user attempting to login
const verifyTokenAuthenticity = async (req, res, next) => {
    const token = req.headers['authentication']
    try {
        // verify token from headers
        const { user } = await jwt.verify(token, SECRET)
        // store user in req
        req.user = user
    } catch(err) {
        console.log(err)
    }
    // proceed
    next()
}

// Graphql
//app.use(verifyTokenAuthenticity)

app.use('/graphql', expressGraphQL(req => ({
    schema,
    graphiql: true,
    // this context is accessible within resolve()
    context: {
        model,
        SECRET,
        user: req.user
    }
})))


// Initial Route
app.get('/', (req, res) => {
    res.sendFile(__dirname + '/index.html')
})

const PORT = process.env.PORT || 8080
app.listen(PORT, () => console.log(chalk.green(`MAGIC STARTS AT PORT ${PORT}`)))

这是我的变异:

const graphql = require('graphql')
const {
    GraphQLObjectType,
    GraphQLString,
    GraphQLNonNull
} = graphql
const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')



const UserType = require('./types/user_type')

const mutation = new GraphQLObjectType({
    name: 'Mutation',
    fields: {
        signup: {
            type: UserType,
            args: { 
                email: { type: new GraphQLNonNull(GraphQLString) },
                password: { type: new GraphQLNonNull(GraphQLString) }
            },
            resolve(parentValue, { email, password }, { model }) {
                return new Promise((resolve, reject) => {
                    model.User.create({
                        email,
                        password
                    })
                    .then(user => {
                        if (!user)
                            return reject('Sorry. something went wrong')
                        resolve(user)
                    })
                    .catch(error => reject(error))
                })
            }
        },
        signin: {
            type: UserType,
            args: {
                email: { type: new GraphQLNonNull(GraphQLString) },
                password: { type: new GraphQLNonNull(GraphQLString) }
            },
            // params: parentValue, args, context
            resolve(parentValue, { email, password }, { model, SECRET }) {
                return new Promise((resolve, reject) => {
                    model.User.find({ where: { email } })
                        .then(user => {
                            if (!user)
                                return reject('Invalid Credentials')
                            if (!bcrypt.compareSync(password, user.password))
                                return reject('Invalid Credentials')

                            const token = jwt.sign({ user: { id: user.id, email: user.email } }, SECRET, { expiresIn: '1yr' })
                            user.token = token  // add token property to user object which will be resolved

                            resolve(user)

                        })
                        .catch(error => reject(error))
                })

            }
        }
    }
})

module.exports = mutation

这是登录页面

import React from 'react'
import gql from 'graphql-tag'
import { graphql } from 'react-apollo'
class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = { email: 'edrren@gmail.com', password: 'password' }
    }
    onSubmit(e) {
        e.preventDefault()
        const { email, password } = this.state
        console.log({ email, password })
        this.props.mutate({
            variables: { email, password }
        }).then(() => console.log(this.props.data))
        .catch(() => console.log("error"))
    }
    render() {
        return (
            <form onSubmit={this.onSubmit.bind(this)}>
                <label>Email</label>
                <input type="text" onChange={(e) => this.setState({ email: e.target.value})} value={this.state.email} />
                <br/>
                <label>Password</label>
                <input type="password" onChange={(e) => this.setState({ password: e.target.value})} value={this.state.password} />
                <button type="submit" >Login</button>
            </form>
        )
    }
}
const mutation = gql`
    mutation SignIn($email: String!, $password: String!){
      signin(email: $email, password: $password) {
        token
      }
    }
`;
export default graphql(mutation)(App)

我的提供者:

import React from 'react'
import ReactDOM from 'react-dom'

import ApolloClient, { createNetworkInterface } from 'apollo-client'
import { ApolloProvider } from 'react-apollo'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import App from './App'
import Header from './components/Header'
import Profile from './components/Profile'
const networkInterface = createNetworkInterface({
  uri: 'http://localhost:8080/graphql'
});
const client = new ApolloClient({
    networkInterface,
    dataIdFromObject: o => o.id
})
const Root = () => {
  return (
    <ApolloProvider client={client}>
      <Router>
          <Switch>
          <Route exact path="/" component={App} />
          <Route path="/profile" component={Profile} />
        </Switch>
      </Router>
    </ApolloProvider>
  )
}
ReactDOM.render(<Root />, document.getElementById('root'));

1 个答案:

答案 0 :(得分:1)

您应在create-react-app项目中使用代理。

package.json主对象中添加以下代理:

"/graphql": {
  "target": "http://localhost:8080/",
  "changeOrigin": true
}

它基本上是将您的请求从“ http://localhost:3000/graphql”代理到“ http://localhost:8080/graphql”。

还可以更改代码以使用相对的API网址。所以现在应该是:

 uri: '/graphql'

这将对您的本地URL进行api调用,节点服务器会将其代理到上述目标。因此,这里没有交叉原点。