React-nodejs 练习 个人博客

1.主要功能模块:

  • 文章管理:CRUD操作
  • 用户系统:注册、登录、权限控制
  • 评论系统:文章评论功能

  • 2.技术栈:

  • 前端:React + Ant Design + React Router
  • 后端:Express + MongoDB
  • 通信:RESTful API + Axios

这个项目采用了典型的MVC架构,前后端完全分离,通过API进行通信。前端负责用户界面和交互,后端负责数据处理和业务逻辑。

3.文件结构

1.  blog-project (前端)


---
├── src/
│   ├── App.jsx                 # 根组件,路由配置
│   ├── App.css                 # 全局样式
│   ├── main.jsx               # 入口文件
│   ├── pages/                 # 页面组件
│   │   ├── Home/             # 首页目录
│   │   │   └── index.jsx     # 首页组件
│   │   ├── Articles/         # 文章相关页面目录
│   │   │   ├── index.jsx     # 文章列表页
│   │   │   └── detail.jsx    # 文章详情页
│   │   └── Login/           # 登录页面目录
│   │       └── index.jsx    # 登录组件
│   ├── components/           # 可复用组件
│   │   └── Layout/          # 布局组件
│   │       └── index.jsx    # 布局组件实现
│   ├── api/                  # API接口
│   │   ├── article.js       # 文章相关API
│   │   ├── user.js          # 用户相关API
│   │   └── request.js       # axios请求封装
│   ├── utils/               # 工具函数
│   └── assets/             # 静态资源
---
└── package.json            # 项目依赖配置

2.  blog-server  (后端)

---
├── src/
│   ├── index.js            # 服务器入口文件
│   ├── routes/             # 路由定义
│   │   ├── postRoutes.js   # 文章路由
│   │   └── userRoutes.js   # 用户路由
│   ├── controllers/        # 控制器
│   │   ├── postController.js # 文章控制器
│   │   └── userController.js # 用户控制器
│   ├── models/            # 数据模型
│   │   ├── post.js        # 文章模型
│   │   └── user.js        # 用户模型
│   ├── middleware/        # 中间件
│   │   └── auth.js        # 认证中间件
│   ├── utils/            # 工具函数
│   └── config/           # 配置文件
---
└── package.json          # 项目依赖配置

4. 流程原理

1. 整体架构思想

前后端分离架构

  • 前端(React + Ant Design)专注于用户界面和交互
  • 后端(Express + MongoDB)专注于数据处理和业务逻辑
  • 通过 HTTP API 进行通信,实现解耦

MVC 架构模式

  • Model(数据模型):MongoDB 的 Schema 定义
  • View(视图):React 组件
  • Controller(控制器):Express 的路由和控制器
   整体工作流程

客户端(React) → 服务端(Express) → 数据库(MongoDB)

  1. 客户端发起请求
  • 用户点击 React Router 的 Link 组件或触发事件
  • React 组件通过 axios 发送 HTTP 请求
  • 请求经过 axios 拦截器处理(添加 token 等)
  1. 服务端接收请求
  • Express 服务器接收请求
  • 通过路由(routes)匹配对应的处理函数
  • 控制器(controllers)处理业务逻辑
  • 与 MongoDB 数据库交互
  1. 数据返回流程
  • MongoDB 返回查询结果
  • Express 处理数据并返回响应
  • axios 拦截器处理响应
  • React 组件更新状态并重新渲染

2 请求流程示例

用户点击文章列表

React Router 的 Link 组件触发路由变化

React 组件加载,调用 axios 请求

axios 拦截器添加 token

请求发送到 Express 服务器

Express 路由匹配到 postRoutes

控制器调用 MongoDB 查询

数据返回给前端

React 组件更新显示

3 关键组件的工作方式

1. axios 拦截器

  • 请求拦截:添加 token、处理请求参数
  • 响应拦截:处理响应数据、处理错误

2. Express 路由

  • 匹配 URL 和 HTTP 方法
  • 调用对应的控制器函数
  • 处理请求参数

3. MongoDB 操作

  • 通过 Mongoose 模型定义数据结构
  • 执行 CRUD 操作
  • 返回查询结果

4.错误处理

前端错误:
React 组件 → axios 拦截器 → 显示错误信息

后端错误:
MongoDB 错误 → Express 控制器 → 返回错误响应 → axios 拦截器 → React 组件显示错误

5.具体代码:

前端部分

App.jsx
import { BrowserRouter as Router, Routes, Route, Link, useNavigate } from 'react-router-dom';
import { Layout, Menu, Button } from 'antd';
import './App.css';
import Login from './pages/Login';
import Home from './pages/Home';
import Articles from './pages/Articles';
import { useState } from 'react';// 后续会创建这些组件
const About = () => <div>关于我</div>;const { Header, Content, Footer } = Layout;function App() {const [isLoggedIn, setIsLoggedIn] = useState(!!localStorage.getItem('token'));return (<Router><Layout className="layout"><Header className="header"><div className="logo">我的个人博客</div><Menutheme="dark"mode="horizontal"defaultSelectedKeys={['/']}items={[{key: '/',label: <Link to="/">首页</Link>,},{key: '/articles',label: <Link to="/articles">文章列表</Link>,},{key: '/about',label: <Link to="/about">关于我</Link>,},]}/>{isLoggedIn ? (<Button onClick={() => {localStorage.removeItem('token');setIsLoggedIn(false);}}>退出</Button>) : (<Link to="/login"><Button type="primary">登录</Button></Link>)}</Header><Content><Routes><Route path="/login" element={<Login />} /><Route path="/" element={<Home />} /><Route path="/articles/*" element={<Articles />} /><Route path="/about" element={<About />} /></Routes></Content><Footer style={{ textAlign: 'center' }}>个人博客 ©2024 Created by shandian</Footer></Layout></Router>);
}export default App;
main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import 'antd/dist/reset.css'
import './index.css'ReactDOM.createRoot(document.getElementById('root')).render(<React.StrictMode><App /></React.StrictMode>,
)
article.js
import request from './request';// 获取文章列表
export const getArticles = () => {return request({url: '/posts',method: 'get'});
};// 获取文章详情
export const getArticleById = (id) => {console.log('Calling API for article:', id); // 添加日志return request({url: `/posts/${id}`,method: 'get'});
};// 创建文章
export const createArticle = (data) => {return request({url: '/posts',method: 'post',data});
};// 更新文章
export const updateArticle = (id, data) => {return request({url: `/posts/${id}`,method: 'put',data});
};// 删除文章
export const deleteArticle = (id) => {return request({url: `/posts/${id}`,method: 'delete'});
};// 获取标签列表
export const getTags = () => {return request({url: '/posts/tags',method: 'get'});
}; 
request.js
import axios from 'axios';
import { message } from 'antd';// 创建axios实例
const request = axios.create({baseURL: 'http://localhost:3000/api',timeout: 5000,headers: {'Content-Type': 'application/json',}
});// 请求拦截器
request.interceptors.request.use(config => {const token = localStorage.getItem('token');if (token) {config.headers.Authorization = `Bearer ${token}`;}// 添加请求日志console.log('Request:', {url: config.url,method: config.method,data: config.data,params: config.params});return config;},error => {console.error('Request Error:', error);return Promise.reject(error);}
);// 响应拦截器
request.interceptors.response.use(response => {// 添加响应日志console.log('Response:', {url: response.config.url,status: response.status,data: response.data});return response.data;},error => {console.error('Response Error:', {url: error.config?.url,status: error.response?.status,data: error.response?.data,message: error.message});if (error.response) {switch (error.response.status) {case 401:message.error('请先登录');localStorage.removeItem('token');window.location.href = '/login';break;case 403:message.error('没有权限');break;case 404:message.error('请求的资源不存在');break;case 500:message.error('服务器错误:' + (error.response.data?.message || '未知错误'));break;default:message.error(`请求错误 (${error.response.status}): ${error.response.data?.message || '未知错误'}`);}} else if (error.request) {message.error('无法连接到服务器,请检查网络连接');} else {message.error('请求配置错误:' + error.message);}return Promise.reject(error);}
);export default request;
user.js
import request from './request';// 用户登录
export const login = (data) => {return request({url: '/users/login',method: 'post',data});
};// 用户注册
export const register = (data) => {return request({url: '/users/register',method: 'post',data});
};// 获取用户信息
export const getUserInfo = () => {return request({url: '/users/info',method: 'get'});
};// 更新用户信息
export const updateUserInfo = (data) => {return request({url: '/user/update',method: 'put',data});
}; 
index.jsx
import { Outlet } from 'react-router-dom';
import Navigation from '../Navigation';function Layout() {return (<div><Navigation /><main><Outlet /></main></div>);
}export default Layout; 
CreateArticle.jsx
import React, { useState } from 'react';
import { Card, Form, Input, Button, Select, message } from 'antd';
import { useNavigate } from 'react-router-dom';
import { createArticle } from '../../api/article';const { TextArea } = Input;const CreateArticle = () => {const [form] = Form.useForm();const [loading, setLoading] = useState(false);const navigate = useNavigate();const onFinish = async (values) => {try {setLoading(true);// 处理标签,将字符串转换为数组const tags = values.tags.split(',').map(tag => tag.trim());await createArticle({...values,tags});message.success('文章创建成功!');navigate('/articles'); // 创建成功后返回文章列表} catch (error) {message.error('创建文章失败:' + (error.message || '未知错误'));} finally {setLoading(false);}};return (<div className="create-article-container"><Card title="创建新文章" className="create-article-card"><Formform={form}layout="vertical"onFinish={onFinish}><Form.Itemname="title"label="文章标题"rules={[{ required: true, message: '请输入文章标题' }]}><Input placeholder="请输入文章标题" /></Form.Item><Form.Itemname="content"label="文章内容"rules={[{ required: true, message: '请输入文章内容' }]}><TextArea rows={15} placeholder="请输入文章内容"style={{ resize: 'none' }}/></Form.Item><Form.Itemname="tags"label="文章标签"help="多个标签请用逗号分隔,如:React,JavaScript,前端"><Input placeholder="请输入标签,用逗号分隔" /></Form.Item><Form.Item><Button type="primary" htmlType="submit" loading={loading} block>发布文章</Button></Form.Item></Form></Card></div>);
};export default CreateArticle;
home.jsx
import React, { useState, useEffect } from 'react';
import { Card, List, Space, Tag, Spin } from 'antd';
import { ClockCircleOutlined, UserOutlined, EyeOutlined } from '@ant-design/icons';
import { Link } from 'react-router-dom';
import { getArticles, getTags } from '../../api/article';
import { getUserInfo } from '../../api/user';
import './index.css';const Home = () => {const [articles, setArticles] = useState([]);const [loading, setLoading] = useState(false);const [userInfo, setUserInfo] = useState(null);const [popularTags, setPopularTags] = useState([]);// 获取最新文章const fetchLatestArticles = async () => {try {setLoading(true);const res = await getArticles({page: 1,pageSize: 5,sort: 'createTime',order: 'desc'});setArticles(res.list);} catch (error) {console.error('获取最新文章失败:', error);} finally {setLoading(false);}};// 获取热门标签const fetchPopularTags = async () => {try {const res = await getTags();setPopularTags(res.slice(0, 10)); // 只显示前10个标签} catch (error) {console.error('获取热门标签失败:', error);}};// 获取用户信息const fetchUserInfo = async () => {try {const res = await getUserInfo();setUserInfo(res);} catch (error) {console.error('获取用户信息失败:', error);}};useEffect(() => {fetchLatestArticles();fetchPopularTags();fetchUserInfo();}, []);return (<div className="home-container"><div className="article-list"><Spin spinning={loading}><ListitemLayout="vertical"size="large"dataSource={articles}renderItem={(item) => (<List.Itemkey={item.id}actions={[<Space><ClockCircleOutlined /> {item.createTime}</Space>,<Space><UserOutlined /> {item.author}</Space>,<Space><EyeOutlined /> {item.views} 次浏览</Space>]}><Card hoverable className="article-card"><Link to={`/article/${item.id}`}><List.Item.Metatitle={<h2>{item.title}</h2>}description={<div><p className="article-summary">{item.summary}</p><Space className="article-tags">{item.tags.map(tag => (<Tag key={tag} color="blue">{tag}</Tag>))}</Space></div>}/></Link></Card></List.Item>)}/></Spin></div><div className="sidebar"><Card title="关于博主" className="about-card">{userInfo ? (<><p>{userInfo.bio || '热爱编程的前端开发者'}</p><p>文章数:{userInfo.articleCount || 0}</p><p>访问量:{userInfo.totalViews || 0}</p></>) : (<p>加载中...</p>)}</Card><Card title="热门标签" className="tags-card"><Space wrap>{popularTags.map(tag => (<Tag key={tag} color="blue"onClick={() => window.location.href = `/articles?tag=${tag}`}style={{ cursor: 'pointer' }}>{tag}</Tag>))}</Space></Card></div></div>);
};export default Home; 
login.jsx
import React, { useState } from 'react';
import { Form, Input, Button, Card, message } from 'antd';
import { UserOutlined, LockOutlined, MailOutlined } from '@ant-design/icons';
import { useNavigate } from 'react-router-dom';
import { login, register } from '../../api/user';
import './index.css';const Login = () => {const [isLogin, setIsLogin] = useState(true);const [loading, setLoading] = useState(false);const navigate = useNavigate();const [form] = Form.useForm();const onFinish = async (values) => {try {setLoading(true);if (isLogin) {// 登录请求const res = await login({email: values.email,password: values.password});// 保存tokenlocalStorage.setItem('token', res.token);message.success('登录成功!');navigate('/'); // 登录成功后跳转到首页} else {// 注册请求if (values.password !== values.confirmPassword) {message.error('两次输入的密码不一致!');return;}await register({username: values.username,email: values.email,password: values.password});message.success('注册成功!');setIsLogin(true); // 注册成功后切换到登录界面form.resetFields();}} catch (error) {// 错误已经在request.js中统一处理console.error('操作失败:', error);} finally {setLoading(false);}};const switchMode = () => {setIsLogin(!isLogin);form.resetFields();};return (<div className="login-container"><Card title={isLogin ? "登录" : "注册"} className="login-card"><Formform={form}name="normal_login"className="login-form"onFinish={onFinish}>{!isLogin && (<Form.Itemname="username"rules={[{ required: true, message: '请输入用户名!' },{ min: 3, message: '用户名至少3个字符!' }]}><Input prefix={<UserOutlined />} placeholder="用户名" /></Form.Item>)}<Form.Itemname="email"rules={[{ required: true, message: '请输入邮箱!' },{ type: 'email', message: '请输入有效的邮箱地址!' }]}><Input prefix={<MailOutlined />} placeholder="邮箱" /></Form.Item><Form.Itemname="password"rules={[{ required: true, message: '请输入密码!' },{ min: 6, message: '密码至少6个字符!' }]}><Inputprefix={<LockOutlined />}type="password"placeholder="密码"/></Form.Item>{!isLogin && (<Form.Itemname="confirmPassword"dependencies={['password']}rules={[{ required: true, message: '请确认密码!' },({ getFieldValue }) => ({validator(_, value) {if (!value || getFieldValue('password') === value) {return Promise.resolve();}return Promise.reject(new Error('两次输入的密码不一致!'));},}),]}><Inputprefix={<LockOutlined />}type="password"placeholder="确认密码"/></Form.Item>)}<Form.Item><Button type="primary" htmlType="submit" className="login-form-button"loading={loading}>{isLogin ? '登录' : '注册'}</Button><Button type="link" onClick={switchMode}>{isLogin ? '还没有账号?去注册' : '已有账号?去登录'}</Button></Form.Item></Form></Card></div>);
};export default Login; 

后端部分

postController.js
const Post = require('../models/post');// 创建新文章
exports.createPost = async (req, res) => {try {const { title, content, tags } = req.body;// 添加调试日志console.log('userId:', req.userId);console.log('request body:', req.body);const post = new Post({title,content,tags,author: req.userId  // 确保这里使用的是 req.userId});const savedPost = await post.save();res.status(201).json({ message: '文章创建成功', post: savedPost });} catch (err) {console.error('Error creating post:', err);  // 添加错误日志res.status(400).json({ message: err.message });}
};// 获取所有文章
exports.getPosts = async (req, res) => {try {console.log('Getting posts...'); // 添加调试日志const posts = await Post.find().populate('author', 'email') // 关联作者信息.sort({ createdAt: -1 }); // 按创建时间倒序console.log('Found posts:', posts); // 添加调试日志res.json(posts);} catch (err) {console.error('Error in getPosts:', err); // 添加错误日志res.status(500).json({ message: err.message });}
};// 获取单个文章
exports.getPost = async (req, res) => {try {const post = await Post.findById(req.params.id).populate('author', 'username email');if (!post) {return res.status(404).json({ message: '文章不存在' });}// 增加阅读量post.views = (post.views || 0) + 1;await post.save();res.json(post);} catch (err) {console.error('获取文章详情失败:', err);res.status(500).json({ message: err.message });}
};// 更新文章
exports.updatePost = async (req, res) => {try {const { title, content, tags, status } = req.body;const post = await Post.findById(req.params.id);if (!post) {return res.status(404).json({ message: '文章不存在' });}// 检查是否是文章作者if (post.author.toString() !== req.userId) {return res.status(403).json({ message: '没有权限修改此文章' });}post.title = title || post.title;post.content = content || post.content;post.tags = tags || post.tags;post.status = status || post.status;await post.save();res.json({ message: '文章更新成功', post });} catch (err) {res.status(400).json({ message: err.message });}
};// 删除文章
exports.deletePost = async (req, res) => {try {const post = await Post.findById(req.params.id);if (!post) {return res.status(404).json({ message: '文章不存在' });}if (post.author.toString() !== req.userId) {return res.status(403).json({ message: '没有权限删除此文章' });}await Post.deleteOne({ _id: req.params.id });res.json({ message: '文章删除成功' });} catch (err) {res.status(500).json({ message: err.message });}
};// 获取所有标签
exports.getTags = async (req, res) => {try {const posts = await Post.find();// 获取所有文章的标签,去重const tags = [...new Set(posts.flatMap(post => post.tags))];res.json(tags);} catch (err) {console.error('Error getting tags:', err);  // 添加错误日志res.status(500).json({ message: err.message });}
};
userController.js
const jwt = require('jsonwebtoken');
const User = require('../models/User');// 注册新用户
exports.register = async (req, res) => {try {const { username, email, password } = req.body;const user = new User({ username, email, password });await user.save();res.status(201).json({ message: '用户创建成功' });} catch (err) {res.status(400).json({ message: err.message });}
};// 登录
exports.login = async (req, res) => {try {const { email, password } = req.body;// 查找用户const user = await User.findOne({ email });if (!user) {return res.status(401).json({ message: '用户不存在' });}// 验证密码const isMatch = await user.comparePassword(password);if (!isMatch) {return res.status(401).json({ message: '密码错误' });}// 生成 JWT tokenconst token = jwt.sign({ userId: user._id },process.env.JWT_SECRET,{ expiresIn: '24h' });// 返回用户信息和 tokenres.json({token,user: {id: user._id,username: user.username,email: user.email,role: user.role}});} catch (err) {res.status(500).json({ message: err.message });}
};// 获取所有用户
exports.getUsers = async (req, res) => {try {const users = await User.find({}, '-password');res.json(users);} catch (err) {res.status(500).json({ message: err.message });}
};// 获取单个用户
exports.getUser = async (req, res) => {try {const user = await User.findById(req.params.id, '-password');if (!user) {return res.status(404).json({ message: '用户不存在' });}res.json(user);} catch (err) {res.status(500).json({ message: err.message });}
};// 获取用户信息
exports.getUserInfo = async (req, res) => {try {console.log('Getting user info for userId:', req.userId); // 添加调试日志const user = await User.findById(req.userId).select('-password');if (!user) {return res.status(404).json({ message: '用户不存在' });}console.log('Found user:', user); // 添加调试日志res.json(user);} catch (err) {console.error('Error in getUserInfo:', err); // 添加错误日志res.status(500).json({ message: err.message });}
};module.exports = exports;

 auth.js
const jwt = require('jsonwebtoken');module.exports = (req, res, next) => {try {// 添加调试日志console.log('Auth Header:', req.headers.authorization);const authHeader = req.headers.authorization;if (!authHeader) {return res.status(401).json({ message: '未提供认证token' });}const token = authHeader.split(' ')[1];console.log('Using JWT_SECRET:', process.env.JWT_SECRET); // 添加调试日志console.log('Extracted token:', token); // 调试日志if (!token) {return res.status(401).json({ message: 'token格式错误' });}// 确保使用正确的 JWT_SECRETconst decoded = jwt.verify(token, process.env.JWT_SECRET);console.log('Decoded token:', decoded); // 调试日志req.userId = decoded.userId;next();} catch (err) {console.error('Auth error:', err); // 错误日志res.status(401).json({ message: '认证失败' });}
};
post.js
const mongoose = require('mongoose');const postSchema = new mongoose.Schema({title: {type: String,required: true,trim: true},content: {type: String,required: true},author: {type: mongoose.Schema.Types.ObjectId,ref: 'User',required: true},tags: [{type: String,trim: true}],status: {type: String,enum: ['draft', 'published'],default: 'published'},views: {type: Number,default: 0}
}, {timestamps: true  // 自动添加 createdAt 和 updatedAt 字段
});const Post = mongoose.model('Post', postSchema);module.exports = Post;
user.js
const bcrypt = require('bcryptjs');const mongoose = require('mongoose');const userSchema = new mongoose.Schema({username: {type: String,required: true,unique: true,trim: true,minlength: 3},email: {type: String,required: true,unique: true,trim: true,lowercase: true},password: {type: String,required: true,minlength: 6},avatar: {type: String,default: ''},role: {type: String,enum: ['user', 'admin'],default: 'user'}
}, {timestamps: true  // 自动添加 createdAt 和 updatedAt 字段
});userSchema.pre('save', async function(next) {if (this.isModified('password')) {this.password = await bcrypt.hash(this.password, 10);}next();
});userSchema.methods.comparePassword = async function(candidatePassword) {return bcrypt.compare(candidatePassword, this.password);
};const User = mongoose.model('User', userSchema);module.exports = User;

postRoutes.js

const express = require('express');
const router = express.Router();
const postController = require('../controllers/postController');
const auth = require('../middleware/auth');// 公开路由
router.get('/tags', postController.getTags);
router.get('/', postController.getPosts);
router.get('/:id', postController.getPost);// 需要认证的路由
router.post('/', auth, postController.createPost);
router.put('/:id', auth, postController.updatePost);
router.delete('/:id', auth, postController.deletePost);module.exports = router;

userRoutes.js

const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');
const auth = require('../middleware/auth');// 公开路由
router.post('/register', userController.register);
router.post('/login', userController.login);// 需要认证的路由
router.get('/info', auth, userController.getUserInfo);
router.get('/', auth, userController.getUsers);
router.get('/:id', auth, userController.getUser);module.exports = router;

处理
const express = require('express');
const cors = require('cors');
const mongoose = require('mongoose');
require('dotenv').config();// 修改这一行,使用正确的相对路径
const userRoutes = require('./routes/userRoutes.js');  // 确保加上 .js 扩展名
const postRoutes = require('./routes/postRoutes.js');  // 添加这行// 连接MongoDB数据库
mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/blog').then(() => console.log('MongoDB 连接成功!')).catch(err => console.error('MongoDB 连接失败:', err));const app = express();// 更详细的 CORS 配置
app.use(cors({origin: 'http://localhost:5173', // 您的前端地址methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],allowedHeaders: ['Content-Type', 'Authorization'],credentials: true
}));app.use(express.json());// 测试路由
app.get('/api/test', (req, res) => {res.json({ message: '后端服务器运行正常!' });
});// 在其他路由之前添加
app.get('/api/health', (req, res) => {res.json({ status: 'ok', timestamp: new Date().toISOString() });
});// 使用路由
app.use('/api/users', userRoutes);
app.use('/api/posts', postRoutes);  // 添加这行// 启动服务器
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {console.log(`服务器运行在 http://localhost:${PORT}`);
});

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Solr-搜索引擎-入门到精通

以下是对 Apache Solr 的简介及其常用语法的快速入门指南&#xff1a; 一、Solr 是什么&#xff1f; • 核心定位&#xff1a;Apache Solr 是一个基于 Lucene 的高性能、开源的搜索平台&#xff0c;支持全文检索、分词、高亮、聚合统计等功能。 • 核心功能&#xff1a; • 全…

Ajax与Axios,以及Apifox的入门使用

Ajax与Axios&#xff0c;以及Apifox的入门使用 作者&#xff1a;blue 时间&#xff1a;2025.3.20 文章目录 Ajax与Axios&#xff0c;以及Apifox的入门使用1.Ajax2.Axios3.Apifox的基本使用内容Path 参数定义语法用途 Query 参数定义语法用途 1.Ajax 概念&#xff1a;Asynchr…

Spring MVC拦截器

一、什么是拦截器 拦截器是 SpringMVC 提供的一种可以在请求处理过程中对请求进行预处理或后处理的机制。简单来说&#xff0c;拦截器就像是一位“守门员”&#xff0c;它拦住所有进来的请求&#xff0c;根据设定的规则决定是否放行或者进行某些操作。 拦截器可以&#xff1a…

mysql语句 聚合+分组+内外链接

1.聚合函数 1.count 记数 2.sum 求和 3.avg *语法&#xff1a;select avg&#xff08;列名&#xff09; from 表名&#xff1b; 4.max 求最大值 5.min 求最小值 求一个班级数学平均分&#xff1f; select avg&#xff08;ifnull&#xff08;math&#xff0c;0&#x…

WPF 与 C# 融合开发:从基础到高级应用(一)

WPF 与 C# 融合开发&#xff1a;从基础到高级应用 一、C# 语言基础回顾 1.1 C# 语言概述 C# 是微软开发的一种现代、面向对象的编程语言&#xff0c;它融合了 C、C 和 Java 等语言的优点&#xff0c;具有简洁、安全、高效等特点。C# 广泛应用于 Windows 平台的应用开发&…

【Linux】IP协议

目录 一、IP协议的概念 二、IP协议的报头 &#xff08;一&#xff09;IP协议报文的封装、解包和分用 &#xff08;二&#xff09;8位生存时间 &#xff08;三&#xff09;IP分片 三、IP协议的网段划分 &#xff08;一&#xff09;为什么需要网段划分 &#xff08;二&am…

如何快速下载并安装 Postman?

从下载、安装、启动 Postman 这三个方面为大家详细讲解下载安装 Postman 每一步操作&#xff0c;帮助初学者快速上手。 Postman 下载及安装教程(2025最新)

计算机网络高频(三)UDP基础

计算机网络高频(三)UDP基础 1.UDP的头部格式是什么样的?⭐ UDP 头部具有以下字段: 源端口(Source Port):16 位字段,表示发送方的端口号。目标端口(Destination Port):16 位字段,表示接收方的端口号。长度(Length):16 位字段,表示 UDP 数据报(包括头部和数据部…

2024年MathorCup数学建模B题甲骨文智能识别中原始拓片单字自动分割与识别研究解题全过程文档加程序

2024年第十四届MathorCup高校数学建模挑战赛 B题 甲骨文智能识别中原始拓片单字自动分割与识别研究 原题再现&#xff1a; 甲骨文是我国目前已知的最早成熟的文字系统&#xff0c;它是一种刻在龟甲或兽骨上的古老文字。甲骨文具有极其重要的研究价值&#xff0c;不仅对中国文…

【深度学习的数学】导数

导数的定义。好像是从极限开始的。比如说&#xff0c;函数f(x)在点xa处的导数&#xff0c;就是当h趋近于0时&#xff0c;[f(ah) - f(a)]除以h的极限&#xff0c;对吧&#xff1f;公式应该是这样的&#xff1a;f’(a) lim_{h→0} [f(ah) - f(a)] / h。这个极限如果存在的话&…

word文件转换为Markdown格式

目录 一、前言1.1、poi-ooxml、docx4j、aspose-words对比二、poi-ooxml技术实现一、前言 顺应时代技术的变更及高效协同理念的影响,非结构化信息展示、存储、应用等也由传统文档向在线协同文档的演变,类似腾讯在线文档。   目前大多数在线文档支持的是Markdown格式,因此这…

【Hugging Face 开源库】Diffusers 库 —— 扩散模型

Diffusers 的三个主要组件1. DiffusionPipeline&#xff1a;端到端推理工具__call__ 函数callback_on_step_end 管道回调函数 2. 预训练模型架构和模块UNetVAE&#xff08;Variational AutoEncoder&#xff09;图像尺寸与 UNet 和 VAE 的关系EMA&#xff08;Exponential Moving…

langserve搭建方法

文章目录 安装 langserver安装 langchain-cli创建langserve脚手架使用poetry管理包 安装 langserver pip install langserve安装 langchain-cli pip install langchain-cli创建langserve脚手架 langchain app new 项目名后续交互界面全回车&#xff0c;接着cd到 项目名 目录…

网络基础-路由器和交换机工作配置

三、路由器和交换机的工作原理配置以及华为体系下的小型网络的搭建 3.1路由基础 3.1.1数据转发 通过链路层交换机和网络层路由器进行数据转发 交换机&#xff08;链路层&#xff09;mac地址表的数据转发路由器&#xff08;网络层&#xff09; ip路由表的数据转发 隔离广播域…

mysql高级,mysql体系结构,mysql引擎,存储过程,索引,锁

1.mysql体系结构 1&#xff09; 连接层 主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念&#xff0c;为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作…

Unity高清渲染管线

Unity高清渲染管线——1 unity高清渲染管线是渲染管线的一种&#xff0c;在看完《创造高清3D虚拟世界》这本书的前两章以及第三张第二小节后终于对unity的高清渲染管线也是有了一个初步的认知&#xff0c;以下是我个人理解仅作参考&#xff1a; unity高清渲染管线项目模板比起…

Python基础语法元素(学习笔记)

实例1&#xff1a;温度转换 # TempConvert.py #为单行注释 多行注释为: 这里写内容 TempStr input("请输入带有符号的温度值&#xff1a;") if TempStr[-1] in [F,f] :C (eval(TempStr[0:-1])-32)/1.8print("转换后的温度是{:.2f}C".format(C)) e…

C++20 中的std::c8rtomb和 std::mbrtoc8

文章目录 1. 引言2. std::c8rtomb 函数详解3. std::mbrtoc8 函数详解4. 使用示例5. 注意事项6. 总结 1. 引言 C20 标准引入了对 UTF-8 编码的更好支持&#xff0c;其中包括两个重要的函数&#xff1a;std::c8rtomb 和 std::mbrtoc8。这两个函数分别用于将 UTF-8 编码的字符转换…

数据可视化TensorboardX和tensorBoard安装及使用

tensorBoard 和TensorboardX 安装及使用指南 tensorBoard 和 TensorBoardX 是用于可视化机器学习实验和模型训练过程的工具。TensorBoard 是 TensorFlow 官方提供的可视化工具&#xff0c;而 TensorBoardX 是其社区驱动的替代品&#xff0c;支持 PyTorch 等其他框架。以下是它…

flutter-实现瀑布流布局及下拉刷新上拉加载更多

文章目录 1. 效果预览2. 结构分析3. 完整代码4. 总结 1. 效果预览 在 Flutter 应用开发中&#xff0c;瀑布流布局常用于展示图片、商品列表等需要以不规则但整齐排列的内容。同时&#xff0c;下拉刷新和上拉加载更多功能&#xff0c;能够极大提升用户体验&#xff0c;让用户方…