Nuxt3 与 Vue3 的 Server api 全栈开发之路

news2024/11/18 9:38:41

Nuxt Server Api

阅读时长:15分钟

本文内容: 国内关于Nuxt3的资料太少了,而Nuxt3又发布了没有多久,导致资料太少。本文浓缩讲解了,对于一个前端开发,上手使用 Nuxt3,并一个人承担前后端开发的所有须知内容

  • [Nuxt3 + Vue3 + Typescript + TaiwindCss 企业级模板]: 即将发布

环境要求:

  • node :v16 +
  • redis : v6 +
export default defineEventHandler(async (event) => {

}
  • Nuxt3.js 官方文档
  • Tailwindcss 文档
  • Antdesign Vue 文档

在这里插入图片描述

起步

  1. 首先要把官方文档关于目录的解释看一遍: nuxt3 目录
  2. 前端页面的开发,只需聚焦目录 pages,所有写法与传统Vue3一模一样.
  3. 后端接口的开发,只需聚焦目录 server, 重点讲解服务端

目录 Layout

一旦设置 Layout,那么文档结构被分为三部分. 头部,content内容主体,页脚

<template>
  <div class="overflow-x-hidden">
    <LayoutPageNavbar class="h-[64px] max-h-[64px]" />
    <LayoutPageContent>
      <slot />
    </LayoutPageContent>
    <LayoutPageFooter class="h-[52px] md:h-[42px]" />
  </div>
</template>

服务端 H3设置

Nuxt3的服务端使用Nitro构建,与H3紧密结合

pnpm add h3
  1. 设置Http状态
import { H3Event } from "h3"
export default defineEventHandler(async (event: H3Event) => {
  setResponseStatus(event, 204)
  return {
    data: '',
    code: 0,
    msg: 'success'
  }
}
  1. 设置 Cookie
const AUTH_COOKIE_NAME = '__session'
const AUTH_COOKIE_MAX_AGE = 60 * 60 * 24 * 5 * 1_000

export default defineEventHandler(async (event: H3Event) => {

  const cookie = 'ffkeifpoeapoifm321654'

  setCookie(event, AUTH_COOKIE_NAME, cookie, {
    maxAge: AUTH_COOKIE_MAX_AGE,
    secure: true,
    httpOnly: true,
    path: '/',
    sameSite: 'lax',
  })

  return {
    data: '',
    code: 0,
    msg: 'success'
  }
}
  1. 存储
interface Query {
  region?: 'zh-CN' | 'en-US' | 'ja-JP' | 'en-AU' | 'en-UK' | 'de-DE' | 'en-NZ' | 'en-CA'
}

export default defineEventHandler(async (event) => {
  const { type = 'img', region = 'zh-CN' } = getQuery<Query>(event)

  if (type === 'img') {
    const cache = await useStorage('cache').getItem('bing-wallpaper')
    if (cache)
      return await sendRedirect(event, cache, 302)
  }

  // https://github.com/zkeq/Bing-Wallpaper-Action
  const data = await (await fetch(`https://raw.onmicrosoft.cn/Bing-Wallpaper-Action/main/data/${region}_all.json`)).json()

  if (type === 'img') {
    const url = `https://bing.com${data.data[0].url}`
    useStorage('cache').setItem('cache:bing-wallpaper', url, { ttl: getTodayRemainMillisecond() })

    return await sendRedirect(event, url, 302)

    // event.node.res.setHeader('Content-Type', 'image/png;charset=utf-8')
    // return Buffer.from(await (await fetch(url)).arrayBuffer())
  }
  else {
    event.context.cache = { ttl: TimeUnitMap.hour }
    return data
  }
})
  1. 验证
import { z, useValidatedBody } from 'h3-zod'
export default defineEventHandler(async event => {
	const body = await useValidatedBody(
		event,
		z.object({
			userId: z.string(),
		})
	)
})
  1. 返回Error
export default defineEventHandler(async (event) => {
  try {
    const cookie = getCookie(event, 'refresh_token') as Token;
    await deleteRefreshToken(cookie);

    return {
      cookie,
    };
  } catch (error) {
    sendError(
      event,
      createError({
        statusCode: 400,
        statusMessage: 'username or password is invalid',
      })
    );
  }

  sendRefreshToken(event, '');

  return {
    message: 'Logout successful',
  };
});
  1. 设置返回数据类型
  1. 返回 audio 类型数据

服务端设置 header

// server/api/speech.ts
export default defineEventHandler(async (event: H3Event) => {
  try {
    // 获取音频数据
    const audioBlob = await fetch_to_audio()

    let size = 0;
    if (audioBlob instanceof Blob) {
      size = audioBlob.size
    }
    setResponseHeader(event, 'Accept-Ranges', 'bytes')
    setResponseHeader(event, 'Content-Type', 'audio/wav')
    setResponseHeader(event, 'Content-Length', size)

    return audioBlob;
  } catch (error) {
    sendError(
      event,
      createError({
        statusCode: 400,
        statusMessage: 'username or password is invalid',
      })
    );
  }
})

前端(客户端)设置数据返回类型为blob

<script lang="ts" setup>
const createAudio = async () => {

  const response: any = await axios({
    url: '/api/speech',
    method: "POST",
    data: {
      content: '123456',
    },
    responseType: "blob"
  })
  const { data: arrayBuffer } = response

  console.log('------收到服务端数据------', response)

  const box = document.querySelector("#audio-box") as HTMLElement
  const audioElement = document.createElement("audio"); //创建标签
  audioElement.autoplay = true
  audioElement.controls = true
  audioElement.src = URL.createObjectURL(arrayBuffer); // 指定链接
  box.appendChild(audioElement)
}
</script>
<template>
  <div>
    <figure id="audio-box">
      <figcaption>音频:</figcaption>
    </figure>
  </div>
</template>
  1. 返回 image 图片类型数据

服务端设置 header

// server/api/image.ts
export default defineEventHandler(async (event: H3Event) => {
  try {
    // 获取图片
    const image = await fetch_to_image()

    let size = 0;
    if (audioBlob instanceof Blob) {
      size = audioBlob.size
    }
    setHeader(e, 'Content-Type', 'image/png')

    return image
  } catch (error) {
    sendError(
      event,
      createError({
        statusCode: 400,
        statusMessage: 'username or password is invalid',
      })
    );
  }
})

SQL使用

  1. postgres 数据库
import { createPool, sql } from '@vercel/postgres'

async function seed() {
  const createTable = await sql`
    CREATE TABLE IF NOT EXISTS users (
      id SERIAL PRIMARY KEY,
      name VARCHAR(255) NOT NULL,
      email VARCHAR(255) UNIQUE NOT NULL,
      image VARCHAR(255),
      "createdAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
    );
    `

  console.log(`Created "users" table`)

  const users = await Promise.all([
    sql`
          INSERT INTO users (name, email, image)
          VALUES ('Guillermo Rauch', 'rauchg@vercel.com', 'https://pbs.twimg.com/profile_images/1576257734810312704/ucxb4lHy_400x400.jpg')
          ON CONFLICT (email) DO NOTHING;
      `,
    sql`
          INSERT INTO users (name, email, image)
          VALUES ('Lee Robinson', 'lee@vercel.com', 'https://pbs.twimg.com/profile_images/1587647097670467584/adWRdqQ6_400x400.jpg')
          ON CONFLICT (email) DO NOTHING;
      `,
    sql`
          INSERT INTO users (name, email, image)
          VALUES ('Steven Tey', 'stey@vercel.com', 'https://pbs.twimg.com/profile_images/1506792347840888834/dS-r50Je_400x400.jpg')
          ON CONFLICT (email) DO NOTHING;
      `,
  ])
  console.log(`Seeded ${users.length} users`)

  return {
    createTable,
    users,
  }
}
export default defineEventHandler(async () => {
  const startTime = Date.now()
  const db = createPool()
  try {
    const { rows: users } = await db.query('SELECT * FROM users')
    const duration = Date.now() - startTime
    return {
      users: users,
      duration: duration,
    }
  } catch (error) {
    // @ts-ignore
    if (error?.message === `relation "users" does not exist`) {
      console.log(
        'Table does not exist, creating and seeding it with dummy data now...'
      )
      // Table is not created yet
      await seed()
      const { rows: users } = await db.query('SELECT * FROM users')
      const duration = Date.now() - startTime
      return {
        users: users,
        duration: duration,
      }
    } else {
      throw error
    }
  }
})
  1. mysql2

安装 Antdesign Vue两种方式

  • 方式一,模块方式
# Using pnpm
pnpm add -D @ant-design-vue/nuxt

nuxt.config.ts

export default defineNuxtConfig({
  modules: [
    '@ant-design-vue/nuxt'
  ],
  antd:{
    // Options
  }
})

使用

<script lang="ts" setup>
const handleMessage = () => {
  message.info("This is a normal message");
}
</script>
<template>
  <a-button @click="handleMessage">
    button
  </a-button>
</template>
  • 方式二,插件方式
# Using pnpm
pnpm add -D ant-design-vue

在plugins文件中新建 awesome.ts

// 1. 引入组件
import Antd from 'ant-design-vue';
// 2. 引入组件样式
import 'ant-design-vue/dist/antd.css';

export default defineNuxtPlugin((nuxt) => {
  nuxt.vueApp.use(Antd);
})

使用

<script lang="ts" setup>
const handleMessage = () => {
  message.info("This is a normal message");
}
</script>
<template>
  <a-button @click="handleMessage">
    button
  </a-button>
</template>

Server Storage 服务端存储

Nuxt的服务端通常只作为转发/代理层,因此数据存储通常也使用轻量的 Redis 存储

  1. 操作系统安装Redis
  2. Redis集成有两种方式

方式一:通过手写代码集成

server/plugins/storage.ts

import redisDriver from 'unstorage/drivers/redis'
export default defineNitroPlugin(() => {
  const storage = useStorage()
  // Dynamically pass in credentials from runtime configuration, or other sources
  const driver = redisDriver({
    base: 'redis',
    host: useRuntimeConfig().redis.host,
    port: useRuntimeConfig().redis.port,
    /* other redis connector options */
  })
  // Mount driver
  storage.mount('redis', driver)
})

nuxt.config.ts

export default defineNuxtConfig({
  runtimeConfig: {
    redis: { // Default values
      host: '',
      port: 0,
      /* other redis connector options */
    }
  }
})

方式二:通过nuxt.内置的nitro服务,nitro.storage

nuxt.config.ts

export default defineNuxtConfig({
  nitro: {
    storage: {
      'redis': {
        driver: 'redis',
        /* redis connector options */
        port: 6379, // Redis port
        host: "127.0.0.1", // Redis host
        username: "", // needs Redis >= 6
        password: "",
        db: 0, // Defaults to 0
        tls: {} // tls/ssl
      }
    }
  }
})

谷歌&github 登录

import { NuxtAuthHandler } from "#auth";
import GithubProvider from "next-auth/providers/github";
import GoogleProvider from "next-auth/providers/google";

const nuxtAuthHandler = NuxtAuthHandler({
  secret: useRuntimeConfig().authSecret,
  providers: [
    // @ts-ignore
    GithubProvider.default({
      clientId: useRuntimeConfig().githubClientId,
      clientSecret: useRuntimeConfig().githubClientSecret,
      httpOptions: {
        timeout: 10000,
      },
    }),
    // @ts-ignore
    GoogleProvider.default({
      clientId: useRuntimeConfig().googleClientId,
      clientSecret: useRuntimeConfig().googleClientSecret,
    }),
  ],
});
export default defineEventHandler(async (event) => {
  const result = await nuxtAuthHandler(event);
  const headerCookies = event.node.res.getHeader("set-cookie");
  if (headerCookies && typeof headerCookies === "string") {
    const sessionToken = headerCookies
      .split(/,(?!\s)/)
      .find((v) => v.includes("session-token"));
    if (sessionToken) {
      event.node.res.removeHeader("set-cookie");
      event.node.res.setHeader("set-cookie", sessionToken);
    }
  }
  return result;
});

参考文档

参考API

  1. H3 API document
  2. H3 github
  3. H3 1.8 API latest intro

参考例子

  1. 例子
  2. About基于Nuxt3 + Vite3 + Vue3 + UnoCSS搭建的API接口服务网站
  3. 参考项目
  4. audio返回数据
  5. Base64 、Blob、File之间的相互转换
  6. NUXT3 server/api params cause 500 error on production)
  7. How to create an AudioBuffer from a Blob
  8. How to return image with server api
  9. How to return audio data with server api

------ 如果文章对你有用,感谢右上角 >>>点赞 | 收藏 <<<

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/946688.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Hive3第六章:更换引擎

系列文章目录 Hive3第一章&#xff1a;环境安装 Hive3第二章&#xff1a;简单交互 Hive3第三章&#xff1a;DML数据操作 Hive3第三章&#xff1a;DML数据操作(二) Hive3第四章&#xff1a;分区表和分桶表 Hive3第五章&#xff1a;函数 Hive3第六章&#xff1a;更换引擎 文章目…

网络安全—0基础学习笔记(黑客)

一、前言 1.这是一条坚持的道路,三分钟的热情可以放弃往下看了. 2.多练多想,不要离开了教程什么都不会了.最好看完教程自己独立完成技术方面的开发. 3.有时多 google,baidu,我们往往都遇不到好心的大神,谁会无聊天天给你做解答. 4.遇到实在搞不懂的,可以先放放,以后再来解决. …

2023年智慧政务一网通办云平台顶层设计与建设方案PPT

导读&#xff1a;原文《2023年智慧政务一网通办云平台顶层设计与建设方案PPT》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 部分内容&#xff1a; 喜欢文章&#…

Spring——RESTful Web服务

文章目录 RESTful Web 服务介绍内容概览下载 Lombok 优化代码利器RESTful Web 服务开发运行项目并测试效果 RESTful Web 服务介绍 本节我们将开发一个简单的 RESTful Web 服务。 RESTful Web 服务与传统的 MVC 开发一个关键区别是返回给客户端的内容的创建方式&#xff1a;传…

Go操作各大消息队列教程(RabbitMQ、Kafka)

Go操作各大消息队列教程 1 RabbitMQ 1.1 概念 ①基本名词 当前市面上mq的产品很多&#xff0c;比如RabbitMQ、Kafka、ActiveMQ、ZeroMQ和阿里巴巴捐献给Apache的RocketMQ。甚至连redis这种NoSQL都支持MQ的功能。 Broker&#xff1a;表示消息队列服务实体Virtual Host&#x…

感觉车载测试的这一波敏捷风快过去了

敏捷&#xff0c;算不得汽车行业的原生产物&#xff0c;几年前&#xff0c;耳边很少听到这个字眼&#xff0c;基本算是在近几年传统汽车行业开始衰落的大背景下&#xff0c;而后伴随着软件从互联网等行业传进来的。 这两年&#xff0c;大家开始把敏捷谈得风生水起&#xff0c;…

用idea查看sqlite数据库idea sqlite

1、安装Database Navigator插件 2、导入数据库并查看 3、删除数据库连接 在此做个笔记

VUE环境下 CSS3+JS 实现发牌 翻牌

创建牌容器&#xff08;关键点&#xff1a;overflow&#xff1a;hidden&#xff09;&#xff1a; <div class"popup-box"></div> .popup-box {position: absolute;width: 100vw;height: 100vh;top: 0px;left: 0;overflow: hidden; } 创建每一张牌《固…

python+TensorFlow实现人脸识别智能小程序的项目(包含TensorFlow版本与Pytorch版本)(一)

pythonTensorFlow实现人脸识别智能小程序的项目&#xff08;包含TensorFlow版本与Pytorch版本&#xff09;&#xff08;一&#xff09; 一&#xff1a;TensorFlow基础知识内容部分&#xff08;简明扼要&#xff0c;快速适应&#xff09;1、下载Cifar10数据集&#xff0c;并进行…

react17:生命周期函数

挂载时更新时 setState触发更新、父组件重新渲染时触发更新forceUpdate触发更新卸载时 react&#xff08;v17.0.2&#xff09;的生命周期图谱如下。 相较于16版本&#xff0c;17版本生命周期函数有如下变化&#xff1a; componentWillMount() componentWillUpdate() compone…

mac电脑屏幕录制Berrycast Mac屏幕录制软件

Berrycast是一款为Mac设计的优秀屏幕录制软件&#xff0c;它让屏幕录制变得简单而高效。以下是Berrycast的一些主要特点&#xff1a; 简单的用户界面&#xff1a;Berrycast拥有直观和简洁的用户界面&#xff0c;使得用户可以轻松上手。高质量的视频输出&#xff1a;Berrycast能…

电商系统架构设计系列(十):怎么能避免写出慢SQL?

上篇文章中&#xff0c;我给你留了一个思考题&#xff1a;怎么能避免写出慢SQL&#xff1f; 我们知道&#xff0c;一个慢 SQL 就可以直接让 MySQL 瘫痪。以我个人经验总结来看&#xff0c;一般情况下系统出问题&#xff0c;大多数都是因为SQL语句的问题。掌握和用好了SQL&…

软件面试题:文件上传下载测试点

目前关于云文档的业务还是挺多的&#xff0c;相信出去面试的同学&#xff0c;大多会遇到这道高频软件测试面试题&#xff1a;文件上传下载测试点。今天向大家分享下&#xff0c;希望对大家有所启发。 一、文件上传测试点 1、文件大小 一般情况下&#xff0c;系统会设定上传文…

基于OV2640/ OV5640 的图像采集显示系统

基于OV2640/ OV5640 的图像采集显示系统系列文章目录&#xff1a; &#xff08;1&#xff09;基于 OV5640 摄像头理论知识讲解-成像和采样原理 &#xff08;2&#xff09;基于 OV5640 摄像头理论知识讲解-数字接口和控制接口 &#xff08;3&#xff09;基于 OV5640 摄像头理论知…

Mac软件删除方法?如何删除不会有残留

Mac电脑如果有太多无用的应用程序&#xff0c;很有可能会拖垮Mac系统的运行速度。因此&#xff0c;卸载电脑中无用的软件是优化Mac系统运行速度的最佳方式之一。Mac卸载应用程序的方式是和Windows有很大的区别&#xff0c;特别对于Mac新用户来说&#xff0c;如何无残留的卸载删…

Python Qt(七)Listview

源代码&#xff1a; # -*- coding: utf-8 -*-# Form implementation generated from reading ui file qt_listview.ui # # Created by: PyQt5 UI code generator 5.15.9 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not…

2024王道408数据结构P144 T18

2024王道408数据结构P144 T18 思考过程 首先还是先看题目的意思&#xff0c;让我们在中序线索二叉树里查找指定结点在后序的前驱结点&#xff0c;这题有一点难至少对我来说…我讲的不清楚理解一下我做的也有点糊涂。在创建结构体时多两个变量ltag和rtag&#xff0c;当ltag0时…

基于Axios完成前后端分离项目数据交互

一、安装Axios npm i axios -S 封装一个请求工具&#xff1a;request.js import axios from axios// 创建可一个新的axios对象 const request axios.create({baseURL: http://localhost:9090, // 后端的接口地址 ip:porttimeout: 30000 })// request 拦截器 // 可以自请求…

全网首发,人体姿态估计算法在OK3588上部署应用(十三)

一、主机模型转换 采用FastDeploy来部署应用深度学习模型到OK3588板卡上 进入主机Ubuntu的虚拟环境 conda activate ok3588 主机环境搭建可以参考上一篇 《OK3588板卡实现人像抠图&#xff08;十二&#xff09;》 生成onnx文件 cd FastDeploy # 下载Paddle静态图模型并解压…

【AGC】集成APMS SDK后台无数据问题

【问题描述】 开发者按照文档集成了APMS SDK&#xff0c;但是在AGC后台没有数据&#xff0c;需要帮忙定位。 【问题分析】 后台没有性能数据的原因有很多&#xff0c;要从端侧和与云侧进行定位分析。 1. 首先需要查看端侧的调试日志&#xff0c;调试日志可以直观的看到性…