由于 CORS 选项预检,ReactJS 无法发送 POST 请求
Posted
技术标签:
【中文标题】由于 CORS 选项预检,ReactJS 无法发送 POST 请求【英文标题】:ReactJS unable to send POST request due to CORS options preflight 【发布时间】:2018-12-02 03:15:07 【问题描述】:每当我向服务器发送POST
请求时,OPTIONS
总是总是被返回。
我有一个使用React
和Apollo
的非常简单的登录应用程序。当我提交表单时,应该使用 POST 请求将mutation
发送到服务器,但 OPTIONS 总是会拦截。
正如您在此处看到的(使用 chrome):
但是当我使用 Firefox 时,在OPTIONS
之后,接下来会请求 POST,如下所示:
我知道我无法禁用预检选项,因为正如我所研究的那样,使用 Content-Type: application/json
/ bodyParser.json()
会触发预检。
但我需要Content-Type: application/json
才能让graphql
工作。但是有什么方法可以只POST 请求而忽略OPTIONS?
我需要 POST 请求返回的 this.props.data
所以我可以将令牌存储在客户端浏览器的 localStorage 中...
我的问题是:
由于 OPTIONS (chrome),POST 请求永远不会发生 我无法在 React 组件中访问this.props.data
,因为
OPTIONS
总是在实际 POST 之前返回FIRST(在 Firefox 中)
我只想从 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'));
【问题讨论】:
你的 UI 的 URL 是什么? @Hriday 是localhost:3000 如果你使用 create-react-app 那么你可以代理你的请求。例如"proxy": "/graphql": "target": "http://localhost:8080"
我可以知道把它放在哪里吗?是的,我正在使用 cra
在 package.json 中,最外层,即在主对象中
【参考方案1】:
您应该在 create-react-app 项目中使用代理。
在package.json
主对象中添加以下代理:
"/graphql":
"target": "http://localhost:8080/",
"changeOrigin": true
它基本上将您的请求从“http://localhost:3000/graphql”代理到“http://localhost:8080/graphql”。
还要更改您的代码以使用相对 api url。所以现在应该是:
uri: '/graphql'
这将对您的本地 url 进行 api 调用,节点服务器会将其代理到上述目标。因此这里不涉及跨域。
【讨论】:
以上是关于由于 CORS 选项预检,ReactJS 无法发送 POST 请求的主要内容,如果未能解决你的问题,请参考以下文章
React:如何强制浏览器为 OPTION 发送特定的标头(预检)
Cors - 如何处理需要自定义标头的预检选项请求? (AWS:使用 vpc 端点的私有 API 网关)
Yii2 和 reactjs CORS 过滤器给出错误:预检响应具有无效的 HTTP 状态代码 401