前言
全栈前后端分离的意义是啥?
- 提高开发效率:前后端分离使得前端和后端开发者可以并行工作,互不干扰。前端专注于用户界面的设计与交互逻辑的实现,而后端专注于数据处理、业务逻辑和服务端功能的开发。这种分工合作大大加快了开发进程。
- 提升代码质量和可维护性:遵循“单一职责”原则,每个部分的代码更加专注和清晰。前端和后端代码的解耦使得代码更容易理解和维护,修改一处不会轻易影响到另一处,降低了系统的复杂度。
- 易于扩展和迭代:分离的架构便于添加新的功能或调整现有功能,因为每个部分都可以相对独立地进行修改。这对于快速迭代的产品开发尤为重要。
- 提高可测试性:分离后的前后端可以独立进行单元测试和集成测试,测试范围更加明确,测试效率和质量得以提升。
- 利于团队协作:清晰的职责划分有助于团队成员之间的沟通和协作,每个人对自己的工作内容有明确的认识,减少了不必要的协调成本。
准备工作
- 新建文件夹
- 文件夹里建子文件
- 对于
frontend文件夹新建一个index.html文件 - 对于
backend文件夹,先在终端输入npm init -y,使其变成后端的项目,继续在终端输入npm i json-server(json-server是一个轻量级的Node.js库,用于快速搭建一个模拟的REST API服务器,适用于前端开发人员在开发阶段,当后端API尚未完成时,用来模拟后端数据交互,以便于前端独立进行开发和测试),再新建一个users.json用于存放用户数据 成功安装json-server时package.json:
- 对于ai_server文件夹,在终端分别输入
npm init -y、npm i openai(OpenAI库是一系列编程库,它们提供了与OpenAI API进行交互的能力,允许开发者利用OpenAI的强大模型来进行自然语言处理、图像生成等多种任务)和npm i dotenv(dotenv是一个简单的零依赖Node.js模块,主要用于管理应用程序的环境变量。它允许开发者创建一个.env文件来存储如数据库凭据、API密钥和其他敏感信息,而不是直接将这些信息硬编码在代码中或者提交到版本控制系统中。dotenv通过读取.env文件并将其中的键值对加载到process.env对象中,使得程序可以在运行时访问这些环境变量),再创建一个.env文件用于存放API密钥,最后创建main.js 成功安装时package.json:
总的文件布局:
正文
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>椰汁33</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row col-md-6 col-md-offset-3">
<h1>AI能力驱动的userData</h1>
<table class="table table-striped" id="user_table">
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>家乡</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div class="row col-md-6 col-md-offset-3">
<form name="aiForm" method="get" action="http://www.baidu.com">
<div class="form-group">
<label for="questionInput">向AI助理提问:</label>
<input type="text" name="question" class="form-control" id="questionInput"
placeholder="请输入您想问的users相关问题">
</div>
<button type="submit" class="btn btn-default">提交</button>
</form>
</div>
<div class="row" id="message">
</div>
</div>
<script>
// 获取存放输出结果的div标签元素
const oMessage = document.querySelector('#message');
// 获取表格的tbody元素
const oBody = document.querySelector('#user_table tbody');
// 获取表单form元素
const oForm = document.forms['aiForm'];
// 定义一个变量usersData用于存放后端的用户数据进行渲染展示
let usersData = [];
// 通过ajax请求进行连接后端的数据
fetch('http://localhost:3000/users')
.then(data => data.json())
.then(users => {
usersData = users;
// console.log(usersData);
// console.log(users);
// 渲染数据
oBody.innerHTML = users.map(user => `
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
<td>${user.hometown}</td>
</tr>
`).join('')
})
oForm.addEventListener('submit', function(event) {
// 阻止表单的默认行为
event.preventDefault();
// 通过name属性去找 性能更好
const question = this["question"].value.trim(); // this.question.value 没有这个性能好
// console.log(question);
// 发送请求
fetch(`http://localhost:8888/users?question=${question}&users=${JSON.stringify(usersData)}`)
.then(data => data.json())
.then(res => {
// console.log(res);
// 渲染输出结果的数据
document.querySelector('#message').innerHTML = res.message;
})
})
</script>
</body>
</html>
此时页面效果为:
users.json(用户数据)
{"users":[
{
"id": 1,
"name": "北北",
"hometown": "抚州"
},
{
"id": 2,
"name": "京京",
"hometown": "赣州"
},
{
"id": 3,
"name": "欢欢",
"hometown": "赣州"
},
{
"id": 4,
"name": "椰汁",
"hometown": "长沙"
},
{
"id": 5,
"name": "言覃",
"hometown": "抚州"
}
]
}
在package.json文件加个dev,实现对用户数据的读取:
启动后端:
- 右键backend文件夹,新建终端
- 在终端输入
npm run dev
- 刷新页面
main.js:
接收HTTP GET请求,解析特定格式的查询参数,利用OpenAI API来生成回答,并以JSON格式响应给客户端,同时设置支持跨域访问。
- 引入必要的模块:
- 定义一个变量http,通过
require('http')引入http模块,http是Node.js的内置模块,用于创建HTTP服务器; - 定义一个变量url,通过
require('url')引入url模块,url解析URL的模块,用于解析请求中的查询参数; - 定义一个变量OpenAI,通过
require('openai')引入openai模块,OpenAI是OpenAI官方提供的Node.js客户端库,用于与OpenAI的API交互; - 引入
dotenv模块,dotenv用于加载.env文件中的环境变量,用于加载OpenAI的API密钥;
const http = require('http');
const url = require('url');
const OpenAI = require('openai');
require('dotenv').config();
- 初始化OpenAI客户端: 使用从环境变量(
process.env.OPENAI_KEY)获取的API密钥来实例化OpenAI客户端:
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
//proxy 代理
baseURL: 'https://api.chatanywhere.tech/v1'
})
- 创建HTTP服务器: 使用
http.createServer()函数来创建一个HTTP服务器。传入的回调函数会在每次接收到请求时被调用。
回调函数:
- 设置跨域头: 为了允许来自不同源的请求,设置了跨域资源共享(CORS)头部,允许任何源(
'*')访问,并指定了允许的请求方法和头部。
// 接收允许跨域
res.setHeader('Access-Control-Allow-Origin', '*'); // 允许所有来源访问,也可以指定具体的域名,如'http://example.com'
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); // 允许的请求方法
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
-
处理/users路径的请求:
- 通过检查
req.url是否包含/users来判断是否是针对AI服务的请求; - 使用
url.parse()解析请求的URL,获取查询参数(question和users); - 构造一个prompt(问题),其中包含了从查询参数得到的用户数据以及要回答的问题;
- 调用OpenAI的ChatCompletion API (
gpt-3.5-turbo模型),传入构造好的prompt,获取AI的回答; - 将AI的回答封装成JSON格式并通过HTTP响应返回给客户端;
- 通过检查
// http 基于请求响应的简单协议 req --> 请求对象 res --> 响应对象
if (req.url.indexOf('/users') >= 0) {
// users ai 服务
const parseUrl = url.parse(req.url, true);
// console.log(parseUrl);
const {question, users} = parseUrl.query;
console.log(question, users);
const prompt = `
${users}
请根据以上用户的json数据,回答${question} 这个问题,
如果回答不了,就返回不清楚
`
const response = await client.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: [{ role: "user", content: prompt }],
temperature: 0, // 控制输出的随机性,0表示更确定的输出
});
const result = response.choices[0].message.content || '';
console.log(result);
let info = {
message:result
}
res.statusCode = 200;
res.setHeader('Content-Type', 'text/json');
res.end(JSON.stringify(info))
}
- 监听端口: 服务器监听8888端口,启动成功后会在控制台打印出“服务器启动成功了”。
server.listen(8888, function () {
console.log('server is running')
})
总代码:
// ai openai :8888/users?question=
// node 内置模块
//- 搭建http服务
const http = require('http');
const url = require('url');
const OpenAI = require('openai');
require('dotenv').config();
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
//proxy 代理
baseURL: 'https://api.chatanywhere.tech/v1'
})
const server = http.createServer(async function (req, res) {
//http 基于请求响应的简单协议 req 请求 res 响应
// if(req)
//console.log(req.url);
if (req.url.indexOf('/users') >= 0) {
res.setHeader('Access-Control-Allow-Origin', '*'); // 允许所有来源访问,也可以指定具体的域名,如'http://example.com'
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); // 允许的请求方法
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
const urlParams = url.parse(req.url, true);
// console.log(urlParams);
const { question, users } = urlParams.query;
console.log(question, users);
const prompt = `
${users}请根据以上JSON数据,回答${question}这个问题,如果回答不了就回答不清楚!
`
const response = await client.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: [{ role: "user", content: prompt }],
temperature: 0, // 控制输出的随机性,0表示更确定的输出
});
const result = response.choices[0].message.content || '';
console.log(result);
let info = {
message: result
}
res.statusCode = 200;
res.setHeader('Content-Type', 'text/json');
res.end(JSON.stringify(info))
}
})
server.listen(8888, function () {
console.log('server is running')
})
.env
存放自己的API密钥,密钥免费获取方法可以前往juejin.cn/post/736912… look
OPENAI_API_KEY="sk-xxxxxxxxx"
- 右键ai_server文件夹,新建终端
- 在终端输入
node main.js
- 页面输入问题
- 页面输出结果
结语
在这个智能技术遍地开花的时代,全栈+AI的组合无疑将为我们的社会和经济带来深远的影响,开启无限可能的未来。总之,全栈与AI的结合,不仅是技术的升级,更是对未来生活的美好憧憬与实践。