当前情况
我正在开发nodejs后端服务器和vue前端应用程序,它们在不同的端口(localhost:3000和localhost:8080)下运行。为了启用CORS连接,我从vue.config.js文件配置了devServer代理。
vue.config.js
module.exports = {
devServer: {
proxy: {
'/users': {
target: 'http://127.0.0.1:3000/users',
changeOrigin: true,
pathRewrite: {
'^/users':''
}
},
'/tasks': {
target: 'http://127.0.0.1:3000/tasks',
changeOrigin: true,
pathRewrite: {
'^/tasks': ''
}
}
}
},
outputDir: '../backend/public'
}
并在技术上使用cors.js启用对后端服务器的请求,该请求由expressjs实现。 我正在发送带有vue组件的请求,以从后端服务器检索数据。通过从服务器获取数据,它可以正常工作,我的目标是在重新加载页面时做出相同的行为。但是,每当我重新加载同一页面时,它始终显示由我自己编写的后端代码设置的401 http响应状态。
立即进行研究和试用
在继续尝试之前,我应该先介绍必须执行的强制性代码。不知何故,这至少是对其中vuex使用axios动作,axios最终使用后端路由器的解释。
tasks.module.js
import axios from "axios"
import authHeader from '../../services/auth-header'
export const tasks = {
state: {
tasks: []
},
getters: {
allTasks: (state) => state.tasks
},
actions: {
async fetchTasks({ commit }) {
const response = await axios.get('http://127.0.0.1:3000/tasks', {headers: authHeader()})
commit('setTasks', response.data)
axios.defaults.headers.common['Authorization'] = authHeader()
},
async addTask({ commit }, description) {
const response = await axios.post('http://127.0.0.1:3000/tasks', { description, completed: false}, {headers: authHeader()})
commit('newTask', response.data)
},
async updateTask({ commit }, updTask) {
const response = await axios.patch('http://127.0.0.1:3000/tasks/'+updTask.id, updTask, {headers: authHeader()})
commit('updateTask', response.data)
}
},
mutations: {
setTasks: (state, tasks) => (state.tasks = tasks),
newTask: (state, task) => state.tasks.unshift(task),
updateTask: (state, updTask) => {
let updates = Object.keys(updTask)
updates.forEach((update) => {
state.task[update] = updTask[update]
})
}
}
}
TaskManager.vue
<template>
<div class="container">
<div class="jumbotron">
<h3>Task Manager</h3>
<AddTask/>
<Tasks/>
</div>
</div>
</template>
<script>
import Tasks from './components/Tasks'
import AddTask from './components/AddTask'
export default {
name:'TaskManager',
components: {
Tasks,
AddTask
}
}
</script>
Tasks.vue
<template>
<div>
<div>
<div class="legend">
<span>Double click to mark as complete</span>
<span>
<span class="incomplete-box"></span> = Incomplete
</span>
<span>
<span class="complete-box"></span> = Complete
</span>
</div>
</div>
<div class="tasks">
<div
@dblclick="onDblClick(task)"
v-for="task in allTasks"
:key="task.id"
class="task"
v-bind:class="{'is-completed':task.completed}">
{{task.description}}
</div>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
export default {
name: "Tasks",
methods:{
...mapActions(['fetchTasks', 'updateTask']),
onDblClick(task) {
const updTask = {
id: task._id,
description: task.description,
completed: !task.completed
}
console.log(updTask)
this.updateTask(updTask)
}
},
computed: {
...mapGetters(['allTasks']),
},
created() {
this.fetchTasks()
}
}
现在我需要介绍解决问题的方法
配置CORS选项
由于此错误页面没有显示应该在请求标头中设置的任何授权标头,因此我弄清楚了启用cors连接的方式,并且我相信这可以启用预检请求。这是我从后端代码配置的中间件行为。 task.js(表达路由器文件)
const router = new express.Router()
const auth = require('../middleware/auth')
const cors = require('cors')
const corsOptions = {
origin: 'http://127.0.0.1:3000',
allowedHeaders: 'content-Type, Authorization',
maxAge:3166950
}
router.options(cors(corsOptions))
router.get('/tasks', auth, async (req, res) => {
const match = {}
const sort = {}
if(req.query.completed) {
match.completed = req.query.completed === 'true'
}
if(req.query.sortBy) {
const parts = req.query.sortBy.split('_')
sort[parts[0]] = parts[1] === 'desc' ? -1:1 // bracket notation
}
try {
await req.user.populate({
path: 'tasks',
match,
options: {
limit: parseInt(req.query.limit),
skip: parseInt(req.query.skip),
sort
}
}).execPopulate()
console.log(req.user.tasks)
res.status(200).send(req.user.tasks)
} catch (e) {
res.status(500).send(e)
}
})
module.exports = router
auth.js(中间件)
const jwt = require('jsonwebtoken')
const User = require('../models/user')
const auth = async (req, res, next) => {
try {
const token = req.header('Authorization').replace('Bearer ','')
const decoded = jwt.verify(token, 'thisisnewcourse')
console.log('decoded token passed')
const user = await User.findOne({ _id: decoded._id, 'tokens.token': token})
console.log('user found')
if(!user) {
throw new Error()
}
req.token = token
req.user = user
next()
} catch (error) {
console.log('error caught')
res.status(401).send({error: 'please authenticate'})
}
}
module.exports = auth
auth.module.js(由于登录正常,我仅复制登录操作部分)
actions: {
async login ({ commit }, user){
try {
const response = await axios.post('http://127.0.0.1:3000/users/login', user)
if(response.data.token){
localStorage.setItem('user', JSON.stringify(response.data))
axios.defaults.headers.common['Authorization'] = `Bearer ${response.data.token}`
}
commit('loginSuccess', response.data)
} catch (e) {
console.log(e)
}
}
我在同一个后端代码(task.js)上尝试了两种不同的中间件
router.get('/tasks', [cors(corsOptions), auth], async (req, res) => {
// same as previously attached code
}
现在,我相信引用another post with similar issue会对我有所帮助,但这是有关启用CORS的问题,而不是没有通过预检请求或其他类型的请求发送标头的问题。
答案 0 :(得分:0)
您尚未包含authHeader
的代码,但我认为它只是返回Authorization
标头的值。
这对我来说似乎很可疑:
async fetchTasks({ commit }) {
const response = await axios.get('http://127.0.0.1:3000/tasks', {headers: authHeader()})
commit('setTasks', response.data)
axios.defaults.headers.common['Authorization'] = authHeader()
},
最后一行似乎试图全局设置Authorization
头,以便将其包含在所有后续axios请求中。很好,但是不尽快做似乎很奇怪。您可以在login
动作中添加类似的代码行,这很有意义,但我认为刷新页面时不会调用该行。
然后有这个位:
{headers: authHeader()}
如果authHeader
返回Authorization
标头的 value ,则此方法将无效。相反,您需要:
{headers: { Authorization: authHeader() }}
理想情况下,您无需在此处设置任何标头,只需在尝试此请求之前设置全局标头即可。
虽然这不是导致问题的直接原因,但您似乎对CORS感到困惑。您已经配置了代理,这意味着您没有使用CORS。您提出的请求来自同一来源,因此CORS不适用。如果您没有发出跨域请求,则无需包含CORS响应标头。如果您确实要发出跨域请求,请不要使用代理。您应该尝试在开发过程中模仿生产环境,因此,如果要在生产中使用CORS,则应在开发过程中使用它。否则,请坚持使用代理。