如何在 Nuxt 中使用私钥 API(在客户端)?

How to use a private API key with Nuxt (on the client)?

问题已解决

如果您遇到同样的问题,请查看已接受的答案,这是使用 serverMiddleware

实现该问题的一种方法

我正在使用需要私钥的 API。我已将密钥存储在 .env 文件中,并在 nuxt 配置文件中调用它,如下所示:

privateRuntimeConfig: {
        secretKey: process.env.MY_SECRET_KEY
},

我的 API 调用是在我的索引页上的 asyncData() 挂钩内完成的。当我加载或重新加载此页面时它工作正常,但每次我使用导航返回此页面时,我都会遇到错误(我使用缓冲区将我的 API 密钥转换为 base64)

First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.

经过一些研究和调试,我发现当时我的私钥不可用,我的 api 调用中使用的“秘密”值是“未定义”。

我不明白的是为什么这在初始加载/重新加载时有效,但在页面导航时无效?有没有办法在不使用后端的情况下修复它? (用于 SEO 的 SSR 和使用私钥而不暴露私钥的能力是我在我的项目中使用 Nuxt 的主要原因)

这是我的代码:

async asyncData({ $content, store, $config }) {
    const secret = Buffer.from($config.secretKey).toString('base64')
    const request = await fetch('https://app.snipcart.com/api/products', {
        headers: {
            'Authorization': `Basic ${secret}`,
            'Accept': 'application/json'
        }
    })
    const result = await request.json()
    store.commit('products/addProducts', result)
    const stocks = store.getters['products/getProducts']



    return { stocks }
},

更新:

当从 API 调用所在的页面以外的任何其他页面访问网站时,它不起作用,因为商店不会有来自 API 调用的任何数据)


好吧,现在我觉得自己很笨。我找到了让它工作的方法。我想花时间解释我的问题有助于我理解如何解决它。

对于那些遇到类似问题的人,我通过用 If 语句包装 API 调用来修复它。

if ($config.secretKey) {
    const secret = Buffer.from($config.secretKey).toString('base64')
    const request = await fetch('https://app.snipcart.com/api/products', {
        headers: {
            'Authorization': `Basic ${secret}`,
            'Accept': 'application/json'
        }
    })
    const result = await request.json()
    store.commit('products/addProducts', result)
}
const stocks = store.getters['products/getProducts']

这样,我可以跳过 API 调用并从我的 vuex 存储中访问值。

更新

查看 @nuxtjs/snipcart module's key 键,因为它是 buildModules,你完全可以把它放在那里,因为它只在构建期间可用(在 Node.js 仅)!

有关更多信息,Snipcart 确实有很多博客文章,这篇基于 Nuxt 的文章可能有助于清理问题:https://www.storyblok.com/tp/how-to-build-a-shop-with-nuxt-storyblok-and-snipcart


你最初确实有你的密钥,因为当你进入页面或硬刷新它时你正在访问服务器。

如果您在 hydration 之后导航,它将是客户端导航,因此您将无法访问私钥。最后,如果您的密钥 确实 私有(现在,一些 API 提供可以公开的密钥),您需要以某些方式解决它。

查看 Snipcart:https://docs.snipcart.com/v3/api-reference/authentication,它明确指出密钥应该在

中可用

Appear in your compiled front-end assets (HTML, JavaScript)

与此同时,如果您需要再次调用后端(尝试访问 products 以外的内容),则需要进行第二次调用。

使用 Nuxt2,您现在无法每次都访问后端,因为您将停留在 SPA 上下文中(Nuxt 是 server then client Vue 应用程序基本上)。但是您可以将令牌写到 cookie 中,甚至更好,使用后端作为代理来隐藏此特定密钥(甚至是无服务器功能)。

可以在我的其他答案中找到更多信息:

感谢@kissu 的(非常)快速回答:)

所以,根据你所说的和你对这个问题的其他回答,我在我的服务器文件夹中用 Nuxt 创建了一个服务器中间件。

server/snipcart.js

const bodyParser = require('body-parser')
const axios = require('axios')
const app = require('express')()

app.use(bodyParser.json())
app.all('/getProducts', (request, response) => {
    
    const url = 'https://app.snipcart.com/api/products'
    const secret = Buffer.from(process.env.SNIPCART_SECRET).toString('base64')
    const config = {
        headers: {
            'Authorization': `Basic ${secret}`,
            'Accept': 'application/json'
        }
    }

    axios
        .get(url, config)
        .then(res => {
            const products = {}  
            res.data.items.forEach(
                item => {
                    const productId = item.userDefinedId.replace(/-/g, '')
                    const stocks = {}

                    item.variants.forEach(
                        variant => {
                            const size = variant.variation[0].option
                            const stock = variant.stock
                            stocks[size] = stock
                        }
                    )
                    products[productId] = stocks
                    
                }
            )
            response.json(products)
        })
        .catch( err => response.json(err) )
})

module.exports = app  

如果我错了请纠正我,但我认为这与使用服务器作为代理基本相同,对吗?基于 Nuxt 生命周期挂钩,serverMiddleware 一个在服务器上只有 运行,所以我的 API 密钥不应该暴露给客户端? (我仍然需要做一些重构来清理代码,但至少它在工作)(https://nuxtjs.org/docs/concepts/nuxt-lifecycle/#server & https://nuxtjs.org/docs/configuration-glossary/configuration-servermiddleware/)

nuxt.config.js

serverMiddleware: [
  { path: "/server", handler: "~/server/snipcart.js" }
]

index.vue(我的 snipcart API 调用是以前进行的,我想现在我应该直接从需要数据的产品卡组件中移动这个调用):

async asyncData({ $content, store, $axios }) {
 
    await $axios
        .get('/server/getProducts')
        .then(res => store.commit('products/addProducts', res.data))
        .catch(err => console.log(err))
 
    const stocks = store.getters['products/getProducts']

    return {stocks, masterplanProducts }
},

PS : Snipcart 确实提供了 public API 键,但是使用非常有限。为了访问每个产品的剩余库存,我必须使用私钥(它允许一些其他操作,比如删除产品/访问订单等)