• 首页

  • 写作

  • 文章归档

  • 照片

  • 友情链接

  • 旅行

  • 读书

  • 日志

  • 随记

  • 人文历史

  • linux

  • 前端
b l o g
b l o g

admin

lzp

01月
11
前端
react ui

基于 cloudflare pages functions 与 vite+react+tailwind 实现 openai 聊天界面

发表于 2025-01-11 • 字数统计 5638 • 被 9 人看爆

##2024/9/28 22:12:18:

基于 cloudflare pages functions 与 vite+react+tailwind 实现 openai 聊天界面

仓库地址:

https://github.com/qyzhizi/js-logical/tree/main/2024-09-26-vite-react-tailwind-chatgpt-demo

cloudflare 地址:https://2024-09-26-vite-react-tailwind-chatgpt-demo.pages.dev/chatgpt

之前在本地实现了 openai 的聊天页面但是只能在本地或者服务器环境上运行,因为 openai key 需要存放在环境变量中,但是 cloudflare 是服务环境,没有本地文件的概念,不能把 key 存放在如 .env 这样文件中。 但是 cloudflare 有自己的环境变量与cloudflare worker 运行时,这样可以把需要依赖环境变量的那部分函数交给 cloudflare page functions 这是cloudflare worker 运行时,可以绑定环境变量,运行函数,访问数据库,可以当成一个轻量的 node.js 后台。

项目结构

在 cloudflare page 中包含两部分,一部分是与前端 ui 相关的,一部分是与 functions (worker)相关的,这里主要探讨 function 的使用,首先在 page 项目中新建 functions 文件夹, 项目结果如下:

├── .gitignore
├── dist
│   ├── assets
│   │   ├── favicon-C49brna2.svg
│   │   ├── index-C_ON94fw.css
│   │   └── index-Db-owgSd.js
│   └── index.html
├── functions
│   └── api
│       ├── azurechatgpt.js
│       └── chatgpt.js
├── index.html
├── package-lock.json
├── package.json
├── postcss.config.js
├── src
│   ├── App.jsx
│   ├── chatgpt.jsx
│   ├── favicon.svg
│   ├── index.css
│   └── main.jsx
├── tailwind.config.js
└── vite.config.js

pages function

以 azurechatgpt.js 例子为例:

export async function onRequestPost(context) {
    const { request, env } = context;

    try {
        // const text = await request.text(); // Correctly read request body
        // Check if the content type is JSON
        const contentType = request.headers.get("content-type");
        let messages = null;
        if (contentType?.includes("application/json")) {
            // Parse the JSON body
            const body = await request.json();
            messages = body.messages;
        } else {
            return new Response('Unsupported Content-Type', { status: 400 });
        }
        
        const requestData = { messages: messages };

        const azure_openai_key = env.AZURE_OPENAI_API_KEY;
        const azure_openai_url = env.AZURE_OPENAI_URL;

        const requestOptions = {
            method: 'POST',
            headers: {
                'api-key': `${azure_openai_key}`,
                "Content-Type": "application/json",
            },
            body: JSON.stringify(requestData),
            // body: requestData,
        };
        const chatres = await fetch(azure_openai_url, requestOptions);
        
        if (!chatres.ok) {
            // Handle non-200 responses
            return new Response(JSON.stringify({ error: "Failed to fetch from OpenAI" }), { status: chatres.status });
        }

        // console.log(chatres)
        const responseData = await chatres.json();
        // console.log(responseData)
        return new Response(JSON.stringify(responseData), { status: 200, headers: { 'Content-Type': 'application/json' } });
    } catch (error) {
        // Catch any errors and return a 500 response
        return new Response(JSON.stringify({ error: "Internal Server Error", details: error.message }), { status: 500 });
    }
}

整个文件就是 一个函数: export async function onRequestPost(context){...} 函数是 onRequestPost 表示接受 POST 请求的异步函数,返回的类型会是 Promise。
调用的 api url: /api/azurechatgpt 可以看到与 azurechatgpt.js 在 functions 目录下的路径对应的,路径是 /api/azurechatgpt.js

在这个函数中用到了 cloudflare 运行时中的环境变量:

const azure_openai_key = env.AZURE_OPENAI_API_KEY;
const azure_openai_url = env.AZURE_OPENAI_URL;

环境变量的设置可以使用配置文件 也可以先将包含项目的 git仓库上传到 cloudflare page上,然后再项目的配置页面上进行设置。
本项目一开始采用的是直接在配置页面上进行手动设置:
https://openai-75050.gzc.vod.tencent-cloud.com/openaiassets_bdd679c319436fe7dc63ad7f61d9281d_2579861727526268561.png
在函数中引用也非常简单:

const { request, env } = context;
env.AZURE_OPENAI_API_KEY;

获取 请求体的代码片段:

// Check if the content type is JSON
const contentType = request.headers.get("content-type");
let text;
if (contentType && contentType.includes("application/json")) {
    // Parse the JSON body
    const body = await request.json();
    text = body.text; // Access the 'text' field
}else {
    return new Response('Unsupported Content-Type', { status: 400 });
}

先判断类型,如果是"application/json", 就进行解析,不过这里依旧采用的是异步方式:const body = await request.json();
text 是请求体中的一个字段

想 azure_openai 发送请求的函数是 fetch, 这是在 cloudflare 中可以使用的 api, 可以比较方便的构造各种请求,在这里发送的是 post 请求
const chatres = await fetch(azure_openai_url, requestOptions); azure_openai_url 不用多说,是请求的 url 地址, requestOptions 是一个对象,包含了请求的类型,请求头,请求体。

const requestOptions = {
    method: 'POST',
    headers: {
        'api-key': `${azure_openai_key}`,
        "Content-Type": "application/json",
    },
    body: JSON.stringify(requestData),
};

${azure_openai_key} 这是一种模板语法
JSON.stringify(requestData) 返回的是 json 化后的字符串

const responseData = await chatres.json(); 表示 json 对象,表示请求返回的结果, 这里也是用异步的方式获取 json 对象

return new Response(JSON.stringify(responseData), { status: 200, headers: { 'Content-Type': 'application/json' } }); 表示最后返回的结果,是整function api 请求的结果。

调用 function 部分的api

接下来是关于ui 部分如何调用 function 部分的api

import React, { useState } from 'react';
import axios from 'axios';

function Chatgpt() {
    const [input, setInput] = useState('');
    const [response, setResponse] = useState('');

    const handleSubmit = async (e) => {
        e.preventDefault();
        if (!input) return;

        try {
            const result = await axios.post('/api/azurechatgpt', {text: input },
                {headers: {'Content-Type': 'application/json', },}
            );
            // Log the result to the console for debugging
            // console.log('Response from API:', result);
            // console.log('Response from API:', result.data.choices[0].message.content);

            setResponse(result.data.choices[0].message.content);
            setInput(''); // Clear the input after submission
        } catch (error) {
            console.error('Error fetching response:', error);
            setResponse('Error fetching response. Please try again.');
        }
    };

    return (
        <div className="text-center">
            <h2 className="text-2xl mb-10">ChatGPT</h2>
            <form onSubmit={handleSubmit}>
                <input
                    type="text"
                    className="border rounded p-2"
                    placeholder="Your question here..."
                    value={input}
                    onChange={(e) => setInput(e.target.value)}
                />
                <button type="submit" className="ml-2 p-2 bg-blue-500 text-white rounded">Ask</button>
            </form>
            {response && (
                <div className="mt-4 p-4 border rounded bg-gray-100">
                    <h3 className="font-bold">Response:</h3>
                    <p>{response}</p>
                </div>
            )}
        </div>
    );
}

export default Chatgpt;

const result = await axios.post('/api/azurechatgpt', {text: input }, 是核心,这里使用的是 axios 异步调用的方式
整个函数 Chatgpt 就是一个 react 组件,将在 App.jsx 中进行组装
result 是一个 json 对象
setResponse(result.data.choices[0].message.content); 表示将返回的结果通过函数 setResponse 赋值到变量 response 中

分享到:
当我提交输入框的信息后, react 是如何更新 UI 界面的?
cloudflare 如何创建一个 react+vite 的前端项目,并发布到 cloudflare
  • 文章目录
  • 站点概览
admin

! lzp

hello

Github Twitter QQ Email Telegram RSS
看爆 Top5
  • 历史与人文 视频链接 189次看爆
  • 2022日志随笔 175次看爆
  • 我的青海湖骑行 164次看爆
  • 读书随笔 124次看爆
  • rs2 设置教程 97次看爆

站点已萌萌哒运行 00 天 00 小时 00 分 00 秒(●'◡'●)ノ♥

Copyright © 2025 admin

由 Halo 强力驱动 · Theme by Sagiri · 站点地图