iT邦幫忙

2023 iThome 鐵人賽

DAY 20
0
影片教學

Nuxt 3 快速入門系列 第 20

[影片教學] Nuxt 3 Cookie 的設置與使用

  • 分享至 

  • xImage
  •  

Yes

👆建議你可以使用影片子母畫面功能或全螢幕播放來獲得最佳的觀賞體驗,👇下方是本篇教學的相關筆記。


使用 useCookie 來設定 Cookie

pages 目錄下建立一個檔案 ./pages/index.vue

<template>
  <div class="flex flex-col items-center justify-center px-4 py-6">
    <div class="flex w-full max-w-md flex-col justify-center">
      <div class="flex flex-col items-center">
        <h2 class="my-4 text-center text-3xl font-bold tracking-tight text-gray-700">Cookie</h2>
      </div>
      <div class="mx-auto flex w-full max-w-xs flex-row justify-around gap-2">
        <div class="flex flex-col items-center gap-2">
          <button
            type="button"
            class="w-fit rounded-sm bg-emerald-500 px-4 py-2 text-sm text-white hover:bg-emerald-600 focus:outline-none focus:ring-2 focus:ring-emerald-400 focus:ring-offset-2"
            @click="setNameCookie"
          >
            設置 name
          </button>
          <div class="flex">
            <label class="text-lg font-semibold text-emerald-500">name:</label>
            <span class="ml-2 flex text-lg text-slate-700">{{ name }}</span>
          </div>
        </div>
        <div class="flex flex-col items-center gap-2">
          <button
            type="button"
            class="w-fit rounded-sm bg-emerald-500 px-4 py-2 text-sm text-white hover:bg-emerald-600 focus:outline-none focus:ring-2 focus:ring-emerald-400 focus:ring-offset-2"
            @click="setCounterCookie"
          >
            設置 counter
          </button>
          <div class="flex">
            <label class="text-lg font-semibold text-emerald-500">counter:</label>
            <span class="ml-2 flex text-lg text-slate-700">{{ counter }}</span>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
const name = useCookie('name')
const counter = useCookie('counter', { maxAge: 10 })

const setNameCookie = () => {
  name.value = 'Ryan'
}

const setCounterCookie = () => {
  counter.value = Math.round(Math.random() * 1000)
}
</script>

修改 app.vue 檔案:

<template>
  <div>
    <NuxtPage />
  </div>
</template>

建立 Server API 取得與設置 Cookie

server/api/ 目錄下建立一個檔案 ./server/api/counter.get.js

export default defineEventHandler((event) => {
  let counter = getCookie(event, 'counter')

  counter = parseInt(counter, 10) || 0
  counter += 1

  setCookie(event, 'counter', counter)

  return { counter }
})

pages 目錄下建立一個檔案 ./pages/index.vue

<template>
  <div class="flex flex-col items-center justify-center px-4 py-6">
    <div class="flex w-full max-w-md flex-col justify-center">
      <div class="flex flex-col items-center">
        <h2 class="my-4 text-center text-3xl font-bold tracking-tight text-gray-700">Cookie</h2>
      </div>
      <div class="mx-auto flex w-full max-w-xs flex-row justify-around gap-2">
        <div class="flex flex-col items-center gap-2">
          <button
            type="button"
            class="w-fit rounded-sm bg-emerald-500 px-4 py-2 text-sm text-white hover:bg-emerald-600 focus:outline-none focus:ring-2 focus:ring-emerald-400 focus:ring-offset-2"
            @click="setNameCookie"
          >
            設置 name
          </button>
          <div class="flex">
            <label class="text-lg font-semibold text-emerald-500">name:</label>
            <span class="ml-2 flex text-lg text-slate-700">{{ name }}</span>
          </div>
        </div>
        <div class="flex flex-col items-center gap-2">
          <button
            type="button"
            class="w-fit rounded-sm bg-emerald-500 px-4 py-2 text-sm text-white hover:bg-emerald-600 focus:outline-none focus:ring-2 focus:ring-emerald-400 focus:ring-offset-2"
            @click="setCounterCookie"
          >
            設置 counter
          </button>
          <div class="flex">
            <label class="text-lg font-semibold text-emerald-500">counter:</label>
            <span class="ml-2 flex text-lg text-slate-700">{{ counter }}</span>
          </div>
        </div>
      </div>

      <div class="mt-2 flex w-full max-w-md flex-col items-center">
        <button
          type="button"
          class="mt-2 w-fit rounded-sm bg-emerald-500 px-4 py-2 text-sm text-white hover:bg-emerald-600 focus:outline-none focus:ring-2 focus:ring-emerald-400 focus:ring-offset-2"
          @click="sendRequest"
        >
          發送 API 請求至 /api/counter
        </button>
      </div>
    </div>
  </div>
</template>

<script setup>
const name = useCookie('name')
const counter = useCookie('counter', { maxAge: 10 })

const setNameCookie = () => {
  name.value = 'Ryan'
}

const setCounterCookie = () => {
  counter.value = Math.round(Math.random() * 1000)
}

const sendRequest = () => {
  useFetch('/api/counter', {
    headers: useRequestHeaders(['cookie'])
  })
}
</script>

修改 app.vue 檔案:

<template>
  <div>
    <NuxtPage />
  </div>
</template>

結合 JWT 驗證身份

安裝 jsonwebtoken 套件

npm install -D jsonwebtoken

建立登入 API 模擬登入產生 JWT Token 與設置 Cookie

server/api/login 目錄下建立一個檔案 ./server/api/login.post.js

import jwt from 'jsonwebtoken'

export default defineEventHandler(async (event) => {
  const body = await readBody(event)

  if (!body.name === 'Ryan') {
    throw createError({
      statusCode: 400,
      statusMessage: '登入失敗'
    })
  }

  const jwtTokenPayload = {
    id: 1,
    nickname: 'Ryan',
    email: 'ryanchien8125@gmail.com'
  }

  const maxAge = 60 * 60 * 24 * 7
  const expires = Math.floor(Date.now() / 1000) + maxAge

  const jwtToken = jwt.sign(
    {
      exp: expires,
      data: jwtTokenPayload
    },
    'JWT_SIGN_SECRET_PLEASE_REPLACE_WITH_YOUR_KEY'
  )

  setCookie(event, 'access_token', jwtToken, {
    maxAge,
    expires: new Date(expires * 1000),
    secure: true,
    httpOnly: true,
    path: '/'
  })

  return '登入成功'
})

pages 目錄下建立一個檔案 ./pages/login.vue

<template>
  <div class="flex flex-col items-center justify-center px-4 py-6">
    <div class="flex w-full max-w-md flex-col justify-center">
      <div class="flex flex-col items-center">
        <h2 class="my-4 text-center text-3xl font-bold tracking-tight text-gray-700">Login</h2>
      </div>
      <div class="mt-2 flex w-full max-w-md flex-col items-center">
        <button
          type="button"
          class="mt-2 w-fit rounded-sm bg-emerald-500 px-4 py-2 text-sm text-white hover:bg-emerald-600 focus:outline-none focus:ring-2 focus:ring-emerald-400 focus:ring-offset-2"
          @click="sendLoginRequest"
        >
          發送 API 請求至 /api/login
        </button>
      </div>
    </div>
  </div>
</template>

<script setup>
const sendLoginRequest = () => {
  useFetch('/api/login', {
    method: 'POST',
    body: {
      name: 'Ryan'
    }
  })
}
</script>

建立驗證 JWT Token Cookie 的 API

server/api 目錄下建立一個檔案 ./server/api/whoami.get.js

import jwt from 'jsonwebtoken'

export default defineEventHandler((event) => {
  const jwtToken = getCookie(event, 'access_token')

  try {
    const { data: userInfo } = jwt.verify(jwtToken, 'JWT_SIGN_SECRET_PLEASE_REPLACE_WITH_YOUR_KEY')

    return {
      id: userInfo.id,
      nickname: userInfo.nickname,
      email: userInfo.email
    }
  } catch (e) {
    throw createError({
      statusCode: 401,
      statusMessage: 'Unauthorized'
    })
  }
})

pages 目錄下建立一個檔案 ./pages/whoami.vue

<template>
  <div class="flex flex-col items-center justify-center px-4 py-6">
    <div class="w-full max-w-md">
      <div class="flex flex-col items-center">
        <h2 class="mt-6 text-center text-3xl font-bold tracking-tight text-gray-700">我是誰</h2>
      </div>
    </div>

    <div class="mt-8 flex w-full max-w-xs flex-col">
      <div v-if="error" class="text-center text-rose-500">{{ error }}</div>
      <template v-if="userInfo">
        <div v-for="key in Object.keys(userInfo)" :key="key" class="mt-1 flex flex-wrap break-all">
          <label class="text-lg font-semibold text-emerald-500"> {{ key }}:</label>
          <span class="ml-2 flex flex-1 text-lg text-slate-700">{{ userInfo[key] }}</span>
        </div>
      </template>
    </div>
  </div>
</template>

<script setup>
const { data: userInfo, error } = await useFetch('/api/whoami', {
  headers: useRequestHeaders(['cookie'])
})
</script>

感謝大家的閱讀,歡迎大家給予建議與討論,也請各位大大鞭小力一些:)
如果對這個 Nuxt 3 系列感興趣,可以訂閱接收通知,也歡迎分享給喜歡或正在學習 Nuxt 3 的夥伴。

範例程式碼

參考資料


上一篇
[影片教學] Nuxt 3 串接 Google OAuth 登入
下一篇
[影片教學] Nuxt 3 使用 I18n 建立多國語系
系列文
Nuxt 3 快速入門30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言