将标头从 Apollo Vue 发送到 Node

Posted

技术标签:

【中文标题】将标头从 Apollo Vue 发送到 Node【英文标题】:Send headers from Apollo Vue to Node 【发布时间】:2021-11-01 00:30:18 【问题描述】:

我正在使用 jwt、apollo-vue 和 graphql 使用 register 和 auth 做一个小 api

我无法通过查询(或通过突变设置)从/到我的后端获取数据。 但我可以从 Postman 那里完成,因为我知道如何在标头中发送令牌。

我也尝试在 vuex 的操作登录下方调用 onLogin(apolloClient, token)。什么都没用

我是后端新手,如有任何建议,我将不胜感激

另一个问题?:如果在下面的函数中...

const authLink = setContext(async (_,  headers ) => 
  // add here console.log(localStorage.getItem('apollo-token'))
  const token = await localStorage.getItem('apollo-token')
  // and then console.log(token)

  return ...
)

第一个控制台打印一个令牌,但第二个控制台打印null。这对我来说很奇怪。

这是我的vue-apollo.js

import Vue from 'vue'
import VueApollo from 'vue-apollo'
import  createApolloClient, restartWebsockets  from 'vue-cli-plugin-apollo/graphql-client'
import  setContext  from 'apollo-link-context'

Vue.use(VueApollo)

const AUTH_TOKEN = 'apollo-token'

// Http endpoint
const httpEndpoint = process.env.VUE_APP_GRAPHQL_HTTP || 'http://localhost:3000/graphql'

const authLink = setContext(async (_,  headers ) => 
  const token = await localStorage.getItem(AUTH_TOKEN)
  return 
    ...headers,
    Authorization: token || ''
  
)

// Files URL root
export const filesRoot = process.env.VUE_APP_FILES_ROOT || httpEndpoint.substr(0, httpEndpoint.indexOf('/graphql'))

Vue.prototype.$filesRoot = filesRoot

// Config
const defaultOptions = 
  httpEndpoint,
  wsEndpoint: null,
  tokenName: AUTH_TOKEN,
  websocketsOnly: false,
  s-s-r: false,
  link: authLink


export const  apolloClient  = createApolloClient(
  ...defaultOptions,
)
 

export function createProvider(options = ) 
  const  apolloClient, wsClient  = createApolloClient(
    ...defaultOptions,
    ...options,
  )
  apolloClient.wsClient = wsClient


  const apolloProvider = new VueApollo(
    defaultClient: apolloClient,
    defaultOptions: 
      $query: 
        // fetchPolicy: 'cache-and-network',
      ,
    ,
    errorHandler(error) 
      // eslint-disable-next-line no-console
      console.log('%cError', 'background: red; color: white; padding: 2px 4px; border-radius: 3px; font-weight: bold;', error.message)
    ,
  )

  return  apolloProvider, apolloClient 


// Manually call this when user log in
export async function onLogin(apolloClient, token) 
  if (typeof localStorage !== 'undefined' && token) 
    localStorage.setItem(AUTH_TOKEN, token)
  
  if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient)
  try 
    await apolloClient.resetStore()
   catch (e) 
    // eslint-disable-next-line no-console
    console.log('%cError on cache reset (login)', 'color: orange;', e.message)
  


// Manually call this when user log out
export async function onLogout(apolloClient) 
  if (typeof localStorage !== 'undefined') 
    localStorage.removeItem(AUTH_TOKEN)
  
  if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient)
  try 
    await apolloClient.resetStore()
   catch (e) 
    // eslint-disable-next-line no-console
    console.log('%cError on cache reset (logout)', 'color: orange;', e.message)
  

来自 vue 的 main.js

// HTTP connection to the API
const httpLink = createHttpLink(
  // You should use an absolute URL here
  uri: 'http://localhost:3000/graphql',
)
// Cache implementation
const cache = new InMemoryCache()

// Create the apollo client
const apolloClient = new ApolloClient(
  link: httpLink,
  cache,
)


Vue.config.productionTip = false
Vue.use(VueScreen)
  .use(VueApollo)

const apolloProvider = new VueApollo(
  defaultClient: apolloClient,
)


new Vue(
  router,
  store,
  vuetify,
  apolloProvider,
  render: h => h(App)
).$mount('#app')

编辑:更多代码

这是查询,在 vue 上的视图

import gql from "graphql-tag";
export default 
  name: "Home",
  apollo: 
    Users: gql`
      
        Users 
          _id
          username
          email
          password
          token
          createdAt
        
      ,
    `,   
  ,
;

我收到的错误是:

bundle.esm.js:75 POST http://localhost:3000/graphql 500(内部服务器错误)

发送查询“用户”服务器错误:响应不成功:收到状态代码 500 在 throwServerError 处

在后台,这是我的查询

Query: 
    async Users(_, req, context) 
        const auth = checkAuth(context)
        if (auth.id) 
            const users = await User.find()
            users.forEach(e => 
                e.password = null
            )
            return users
         else 
            return new Error("must be logged.")
        
    ,

这是我的 checkAuth.js

import jwt from 'jsonwebtoken'
import  AuthenticationError  from 'apollo-server'
import 'dotenv/config'


module.exports = (context) => 
    const authHeader = context.headers.authorization;
    console.log("headers: ",context.headers)
    if (authHeader) 
        const token = authHeader.split('Bearer ')[1];
        if (token) 
            try 
                const user = jwt.verify(token, process.env.SECRET_KEY);
                return user
             catch (err) 
                return new AuthenticationError("Invalid token.")
            
        
        return new Error("Token must be 'Bearer [token]'")
    
    return new Error("I need a token bro!")

编辑 2

后端收到的 context.header

headers: 
    host: 'localhost:3000',
    connection: 'keep-alive',
    'content-length': '160',
    'sec-ch-ua': '"Chromium";v="92", " Not A;Brand";v="99", "Google Chrome";v="92"',
    accept: '*/*',
    'sec-ch-ua-mobile': '?0',
    'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/92.0.4515.159 Safari/537.36',
    'content-type': 'application/json',
    origin: 'http://localhost:8081',
    'sec-fetch-site': 'same-site',
    'sec-fetch-mode': 'cors',
    'sec-fetch-dest': 'empty',
    referer: 'http://localhost:8081/',
    'accept-encoding': 'gzip, deflate, br',
    'accept-language': 'es-419,es;q=0.9,en;q=0.8'
  ,

【问题讨论】:

当你“说什么都没用”时,你能说得更清楚吗?如果你看看控制台。执行查询时会打印什么错误? 是的,很抱歉。该错误仅发生在前端。 Postman 中的相同查询,但在标头中有标记,工作正常。错误和更多代码现在添加到问题下方。 @Joyescat 也许有某种方法可以从前端的查询中发送 Apollo 对象中的标头? 后端日志打印什么错误?是“无效令牌。”、“令牌必须是 'Bearer [token]'”或“我需要令牌兄弟!”触发错误? “有某种方法可以从前端的查询中发送 Apollo 对象中的标头?”是什么意思?您已经在vue-apollo.js 中的setContext 函数中发送带有令牌的标头 【参考方案1】:

据我所知,您只在授权标头中发送令牌。

const authLink = setContext(async (_,  headers ) => 
  const token = await localStorage.getItem(AUTH_TOKEN)
  return 
    ...headers,
    Authorization: token || ''
  
)

但您希望在后端找到不记名令牌:


module.exports = (context) => 
    const authHeader = context.headers.authorization;
    console.log("headers: ",context.headers)
    if (authHeader) 
        const token = authHeader.split('Bearer ')[1]; << Your code is breaking here
        if (token) 
            try 
                const user = jwt.verify(token, process.env.SECRET_KEY);
                return user
             catch (err) 
                return new AuthenticationError("Invalid token.")
            
        
        return new Error("Token must be 'Bearer [token]'")
    
    return new Error("I need a token bro!")

您必须发送 'Bearer [token]' 而不仅仅是令牌。像这样:

const authLink = setContext(async (_,  headers ) => 
  const token = await localStorage.getItem(AUTH_TOKEN)
  return 
    ...headers,
    Authorization: `Bearer $token`
  
)

【讨论】:

【参考方案2】:

在documentation 中,setContext 是这样使用的:

const setAuthorizationLink = setContext((request, previousContext) => (
  headers: authorization: "1234"
));

setContext 函数采用返回对象的函数或返回对象的 Promise 来设置请求的新上下文。

在下面的代码中,您只返回标题。当你应该返回上下文时。

const authLink = setContext(async (_,  headers ) => 
  const token = await localStorage.getItem(AUTH_TOKEN)
  return 
    ...headers,
    Authorization: token || ''
  
)

试试这个

const authLink = setContext(async (_,  headers ) => 
  const token = await localStorage.getItem(AUTH_TOKEN)
  return 
    headers: 
      ...headers,
      Authorization: token || ''
    
  
)

【讨论】:

它也没有用。我在 checkAuth.js 中收到的 context.header 没有授权密钥。【参考方案3】:

vue-apollo.js 文件未使用。

在您的main.js 中,您在Vue 中注入的apolloClientmain.js 中声明,并且不包含authLink你在vue-apollo.js中的所有代码没有被调用

所以不要这样:

// HTTP connection to the API
const httpLink = createHttpLink(
  // You should use an absolute URL here
  uri: 'http://localhost:3000/graphql',
)
// Cache implementation
const cache = new InMemoryCache()

// Create the apollo client
const apolloClient = new ApolloClient(
  link: httpLink,
  cache,
)


Vue.config.productionTip = false
Vue.use(VueScreen)
  .use(VueApollo)

const apolloProvider = new VueApollo(
  defaultClient: apolloClient,
)


new Vue(
  router,
  store,
  vuetify,
  apolloProvider,
  render: h => h(App)
).$mount('#app')

试试这个:

import  createProvider  from 'vue-apollo.js';

Vue.config.productionTip = false
Vue.use(VueScreen)
  .use(VueApollo)

const  apolloProvider, apolloClient  = createProvider();

new Vue(
  router,
  store,
  vuetify,
  apolloProvider,
  render: h => h(App)
).$mount('#app')

【讨论】:

天哪,非常感谢。你拯救了我的一天,梅西祝福你

以上是关于将标头从 Apollo Vue 发送到 Node的主要内容,如果未能解决你的问题,请参考以下文章

apollo-client 没有响应的标头

如何将 cookie 添加到 vue-apollo 请求中?

Post Request express:发送到客户端后无法设置标头

GraphQL:无法读取 Apollo 客户端中的响应标头

如何从 graphQL apollo 突变或查询上的 cookie 更新授权标头

Node.js,Express:将标头发送到客户端后无法设置标头